forked from kscience/kmath
[WIP] Refactor optimization
This commit is contained in:
parent
2cf56641aa
commit
257337f4fb
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
package space.kscience.kmath.functions
|
package space.kscience.kmath.functions
|
||||||
|
|
||||||
import space.kscience.kmath.integration.integrate
|
import space.kscience.kmath.integration.process
|
||||||
import space.kscience.kmath.integration.value
|
import space.kscience.kmath.integration.value
|
||||||
import space.kscience.kmath.operations.DoubleField
|
import space.kscience.kmath.operations.DoubleField
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
@ -15,7 +15,7 @@ fun main() {
|
|||||||
val function: UnivariateFunction<Double> = { x -> 3 * x.pow(2) + 2 * x + 1 }
|
val function: UnivariateFunction<Double> = { x -> 3 * x.pow(2) + 2 * x + 1 }
|
||||||
|
|
||||||
//get the result of the integration
|
//get the result of the integration
|
||||||
val result = DoubleField.integrate(0.0..10.0, function = function)
|
val result = DoubleField.process(0.0..10.0, function = function)
|
||||||
|
|
||||||
//the value is nullable because in some cases the integration could not succeed
|
//the value is nullable because in some cases the integration could not succeed
|
||||||
println(result.value)
|
println(result.value)
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
package space.kscience.kmath.functions
|
package space.kscience.kmath.functions
|
||||||
|
|
||||||
import space.kscience.kmath.integration.integrate
|
import space.kscience.kmath.integration.process
|
||||||
import space.kscience.kmath.integration.value
|
import space.kscience.kmath.integration.value
|
||||||
import space.kscience.kmath.nd.StructureND
|
import space.kscience.kmath.nd.StructureND
|
||||||
import space.kscience.kmath.nd.nd
|
import space.kscience.kmath.nd.nd
|
||||||
@ -24,7 +24,7 @@ fun main(): Unit = DoubleField {
|
|||||||
val function: (Double) -> StructureND<Double> = { x: Double -> 3 * number(x).pow(2) + 2 * diagonal(x) + 1 }
|
val function: (Double) -> StructureND<Double> = { x: Double -> 3 * number(x).pow(2) + 2 * diagonal(x) + 1 }
|
||||||
|
|
||||||
//get the result of the integration
|
//get the result of the integration
|
||||||
val result = integrate(0.0..10.0, function = function)
|
val result = process(0.0..10.0, function = function)
|
||||||
|
|
||||||
//the value is nullable because in some cases the integration could not succeed
|
//the value is nullable because in some cases the integration could not succeed
|
||||||
println(result.value)
|
println(result.value)
|
||||||
|
@ -24,7 +24,7 @@ public class CMIntegrator(
|
|||||||
public class MinIterations(public val value: Int) : IntegrandFeature
|
public class MinIterations(public val value: Int) : IntegrandFeature
|
||||||
public class MaxIterations(public val value: Int) : IntegrandFeature
|
public class MaxIterations(public val value: Int) : IntegrandFeature
|
||||||
|
|
||||||
override fun integrate(integrand: UnivariateIntegrand<Double>): UnivariateIntegrand<Double> {
|
override fun process(integrand: UnivariateIntegrand<Double>): UnivariateIntegrand<Double> {
|
||||||
val integrator = integratorBuilder(integrand)
|
val integrator = integratorBuilder(integrand)
|
||||||
val maxCalls = integrand.getFeature<IntegrandMaxCalls>()?.maxCalls ?: defaultMaxCalls
|
val maxCalls = integrand.getFeature<IntegrandMaxCalls>()?.maxCalls ?: defaultMaxCalls
|
||||||
val remainingCalls = maxCalls - integrand.calls
|
val remainingCalls = maxCalls - integrand.calls
|
||||||
@ -32,11 +32,12 @@ public class CMIntegrator(
|
|||||||
?: error("Integration range is not provided")
|
?: error("Integration range is not provided")
|
||||||
val res = integrator.integrate(remainingCalls, integrand.function, range.start, range.endInclusive)
|
val res = integrator.integrate(remainingCalls, integrand.function, range.start, range.endInclusive)
|
||||||
|
|
||||||
return integrand +
|
return integrand.with(
|
||||||
IntegrandValue(res) +
|
IntegrandValue(res),
|
||||||
IntegrandAbsoluteAccuracy(integrator.absoluteAccuracy) +
|
IntegrandAbsoluteAccuracy(integrator.absoluteAccuracy),
|
||||||
IntegrandRelativeAccuracy(integrator.relativeAccuracy) +
|
IntegrandRelativeAccuracy(integrator.relativeAccuracy),
|
||||||
IntegrandCallsPerformed(integrator.evaluations + integrand.calls)
|
IntegrandCallsPerformed(integrator.evaluations + integrand.calls)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ public class GaussRuleIntegrator(
|
|||||||
private var type: GaussRule = GaussRule.LEGANDRE,
|
private var type: GaussRule = GaussRule.LEGANDRE,
|
||||||
) : UnivariateIntegrator<Double> {
|
) : UnivariateIntegrator<Double> {
|
||||||
|
|
||||||
override fun integrate(integrand: UnivariateIntegrand<Double>): UnivariateIntegrand<Double> {
|
override fun process(integrand: UnivariateIntegrand<Double>): UnivariateIntegrand<Double> {
|
||||||
val range = integrand.getFeature<IntegrationRange>()?.range
|
val range = integrand.getFeature<IntegrationRange>()?.range
|
||||||
?: error("Integration range is not provided")
|
?: error("Integration range is not provided")
|
||||||
val integrator: GaussIntegrator = getIntegrator(range)
|
val integrator: GaussIntegrator = getIntegrator(range)
|
||||||
@ -76,7 +76,7 @@ public class GaussRuleIntegrator(
|
|||||||
numPoints: Int = 100,
|
numPoints: Int = 100,
|
||||||
type: GaussRule = GaussRule.LEGANDRE,
|
type: GaussRule = GaussRule.LEGANDRE,
|
||||||
function: (Double) -> Double,
|
function: (Double) -> Double,
|
||||||
): Double = GaussRuleIntegrator(numPoints, type).integrate(
|
): Double = GaussRuleIntegrator(numPoints, type).process(
|
||||||
UnivariateIntegrand(function, IntegrationRange(range))
|
UnivariateIntegrand(function, IntegrationRange(range))
|
||||||
).value!!
|
).value!!
|
||||||
}
|
}
|
||||||
|
@ -14,10 +14,7 @@ import org.apache.commons.math3.optim.nonlinear.scalar.gradient.NonLinearConjuga
|
|||||||
import org.apache.commons.math3.optim.nonlinear.scalar.noderiv.AbstractSimplex
|
import org.apache.commons.math3.optim.nonlinear.scalar.noderiv.AbstractSimplex
|
||||||
import org.apache.commons.math3.optim.nonlinear.scalar.noderiv.NelderMeadSimplex
|
import org.apache.commons.math3.optim.nonlinear.scalar.noderiv.NelderMeadSimplex
|
||||||
import org.apache.commons.math3.optim.nonlinear.scalar.noderiv.SimplexOptimizer
|
import org.apache.commons.math3.optim.nonlinear.scalar.noderiv.SimplexOptimizer
|
||||||
import space.kscience.kmath.expressions.DifferentiableExpression
|
import space.kscience.kmath.expressions.*
|
||||||
import space.kscience.kmath.expressions.Expression
|
|
||||||
import space.kscience.kmath.expressions.SymbolIndexer
|
|
||||||
import space.kscience.kmath.expressions.derivative
|
|
||||||
import space.kscience.kmath.misc.Symbol
|
import space.kscience.kmath.misc.Symbol
|
||||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||||
import space.kscience.kmath.optimization.*
|
import space.kscience.kmath.optimization.*
|
||||||
@ -26,94 +23,98 @@ import kotlin.reflect.KClass
|
|||||||
public operator fun PointValuePair.component1(): DoubleArray = point
|
public operator fun PointValuePair.component1(): DoubleArray = point
|
||||||
public operator fun PointValuePair.component2(): Double = value
|
public operator fun PointValuePair.component2(): Double = value
|
||||||
|
|
||||||
|
public class CMOptimizerFactory(public val optimizerBuilder: () -> MultivariateOptimizer) : OptimizationFeature
|
||||||
|
//public class CMOptimizerData(public val )
|
||||||
|
|
||||||
@OptIn(UnstableKMathAPI::class)
|
@OptIn(UnstableKMathAPI::class)
|
||||||
public class CMOptimization(
|
public class CMOptimization : Optimizer<FunctionOptimization<Double>> {
|
||||||
override val symbols: List<Symbol>,
|
|
||||||
) : FunctionOptimization<Double>, NoDerivFunctionOptimization<Double>, SymbolIndexer, OptimizationFeature {
|
|
||||||
|
|
||||||
private val optimizationData: HashMap<KClass<out OptimizationData>, OptimizationData> = HashMap()
|
override suspend fun process(
|
||||||
private var optimizerBuilder: (() -> MultivariateOptimizer)? = null
|
problem: FunctionOptimization<Double>
|
||||||
public var convergenceChecker: ConvergenceChecker<PointValuePair> = SimpleValueChecker(
|
): FunctionOptimization<Double> = withSymbols(problem.parameters){
|
||||||
DEFAULT_RELATIVE_TOLERANCE,
|
val cmOptimizer: MultivariateOptimizer =
|
||||||
DEFAULT_ABSOLUTE_TOLERANCE,
|
problem.getFeature<CMOptimizerFactory>()?.optimizerBuilder?.invoke() ?: SimplexOptimizer()
|
||||||
DEFAULT_MAX_ITER
|
|
||||||
)
|
|
||||||
|
|
||||||
override var maximize: Boolean
|
val convergenceChecker: ConvergenceChecker<PointValuePair> = SimpleValueChecker(
|
||||||
get() = optimizationData[GoalType::class] == GoalType.MAXIMIZE
|
DEFAULT_RELATIVE_TOLERANCE,
|
||||||
set(value) {
|
DEFAULT_ABSOLUTE_TOLERANCE,
|
||||||
optimizationData[GoalType::class] = if (value) GoalType.MAXIMIZE else GoalType.MINIMIZE
|
DEFAULT_MAX_ITER
|
||||||
|
)
|
||||||
|
|
||||||
|
val optimizationData: HashMap<KClass<out OptimizationData>, OptimizationData> = HashMap()
|
||||||
|
|
||||||
|
fun addOptimizationData(data: OptimizationData) {
|
||||||
|
optimizationData[data::class] = data
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun addOptimizationData(data: OptimizationData) {
|
|
||||||
optimizationData[data::class] = data
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
addOptimizationData(MaxEval.unlimited())
|
addOptimizationData(MaxEval.unlimited())
|
||||||
}
|
addOptimizationData(InitialGuess(problem.initialGuess.toDoubleArray()))
|
||||||
|
|
||||||
public fun exportOptimizationData(): List<OptimizationData> = optimizationData.values.toList()
|
fun exportOptimizationData(): List<OptimizationData> = optimizationData.values.toList()
|
||||||
|
|
||||||
public override fun initialGuess(map: Map<Symbol, Double>): Unit {
|
|
||||||
addOptimizationData(InitialGuess(map.toDoubleArray()))
|
|
||||||
}
|
|
||||||
|
|
||||||
public override fun function(expression: Expression<Double>): Unit {
|
/**
|
||||||
val objectiveFunction = ObjectiveFunction {
|
* Register no-deriv function instead of differentiable function
|
||||||
val args = it.toMap()
|
*/
|
||||||
expression(args)
|
/**
|
||||||
|
* Register no-deriv function instead of differentiable function
|
||||||
|
*/
|
||||||
|
fun noDerivFunction(expression: Expression<Double>): Unit {
|
||||||
|
val objectiveFunction = ObjectiveFunction {
|
||||||
|
val args = problem.initialGuess + it.toMap()
|
||||||
|
expression(args)
|
||||||
|
}
|
||||||
|
addOptimizationData(objectiveFunction)
|
||||||
}
|
}
|
||||||
addOptimizationData(objectiveFunction)
|
|
||||||
}
|
|
||||||
|
|
||||||
public override fun diffFunction(expression: DifferentiableExpression<Double, Expression<Double>>) {
|
public override fun function(expression: DifferentiableExpression<Double, Expression<Double>>) {
|
||||||
function(expression)
|
noDerivFunction(expression)
|
||||||
val gradientFunction = ObjectiveFunctionGradient {
|
val gradientFunction = ObjectiveFunctionGradient {
|
||||||
val args = it.toMap()
|
val args = startingPoint + it.toMap()
|
||||||
DoubleArray(symbols.size) { index ->
|
DoubleArray(symbols.size) { index ->
|
||||||
expression.derivative(symbols[index])(args)
|
expression.derivative(symbols[index])(args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addOptimizationData(gradientFunction)
|
||||||
|
if (optimizerBuilder == null) {
|
||||||
|
optimizerBuilder = {
|
||||||
|
NonLinearConjugateGradientOptimizer(
|
||||||
|
NonLinearConjugateGradientOptimizer.Formula.FLETCHER_REEVES,
|
||||||
|
convergenceChecker
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addOptimizationData(gradientFunction)
|
|
||||||
if (optimizerBuilder == null) {
|
public fun simplex(simplex: AbstractSimplex) {
|
||||||
optimizerBuilder = {
|
addOptimizationData(simplex)
|
||||||
NonLinearConjugateGradientOptimizer(
|
//Set optimization builder to simplex if it is not present
|
||||||
NonLinearConjugateGradientOptimizer.Formula.FLETCHER_REEVES,
|
if (optimizerBuilder == null) {
|
||||||
convergenceChecker
|
optimizerBuilder = { SimplexOptimizer(convergenceChecker) }
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public fun simplex(simplex: AbstractSimplex) {
|
public fun simplexSteps(steps: Map<Symbol, Double>) {
|
||||||
addOptimizationData(simplex)
|
simplex(NelderMeadSimplex(steps.toDoubleArray()))
|
||||||
//Set optimization builder to simplex if it is not present
|
|
||||||
if (optimizerBuilder == null) {
|
|
||||||
optimizerBuilder = { SimplexOptimizer(convergenceChecker) }
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public fun simplexSteps(steps: Map<Symbol, Double>) {
|
public fun goal(goalType: GoalType) {
|
||||||
simplex(NelderMeadSimplex(steps.toDoubleArray()))
|
addOptimizationData(goalType)
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun goal(goalType: GoalType) {
|
public fun optimizer(block: () -> MultivariateOptimizer) {
|
||||||
addOptimizationData(goalType)
|
optimizerBuilder = block
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun optimizer(block: () -> MultivariateOptimizer) {
|
override fun update(result: OptimizationResult<Double>) {
|
||||||
optimizerBuilder = block
|
initialGuess(result.point)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun update(result: OptimizationResult<Double>) {
|
override suspend fun optimize(): OptimizationResult<Double> {
|
||||||
initialGuess(result.point)
|
val optimizer = optimizerBuilder?.invoke() ?: error("Optimizer not defined")
|
||||||
}
|
val (point, value) = optimizer.optimize(*optimizationData.values.toTypedArray())
|
||||||
|
return OptimizationResult(point.toMap(), value)
|
||||||
override fun optimize(): OptimizationResult<Double> {
|
}
|
||||||
val optimizer = optimizerBuilder?.invoke() ?: error("Optimizer not defined")
|
return@withSymbols TODO()
|
||||||
val (point, value) = optimizer.optimize(*optimizationData.values.toTypedArray())
|
|
||||||
return OptimizationResult(point.toMap(), value, setOf(this))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public companion object : OptimizationProblemFactory<Double, CMOptimization> {
|
public companion object : OptimizationProblemFactory<Double, CMOptimization> {
|
||||||
|
@ -10,10 +10,7 @@ import space.kscience.kmath.commons.expressions.DerivativeStructureField
|
|||||||
import space.kscience.kmath.expressions.DifferentiableExpression
|
import space.kscience.kmath.expressions.DifferentiableExpression
|
||||||
import space.kscience.kmath.expressions.Expression
|
import space.kscience.kmath.expressions.Expression
|
||||||
import space.kscience.kmath.misc.Symbol
|
import space.kscience.kmath.misc.Symbol
|
||||||
import space.kscience.kmath.optimization.FunctionOptimization
|
import space.kscience.kmath.optimization.*
|
||||||
import space.kscience.kmath.optimization.OptimizationResult
|
|
||||||
import space.kscience.kmath.optimization.noDerivOptimizeWith
|
|
||||||
import space.kscience.kmath.optimization.optimizeWith
|
|
||||||
import space.kscience.kmath.structures.Buffer
|
import space.kscience.kmath.structures.Buffer
|
||||||
import space.kscience.kmath.structures.asBuffer
|
import space.kscience.kmath.structures.asBuffer
|
||||||
|
|
||||||
@ -46,20 +43,25 @@ public fun FunctionOptimization.Companion.chiSquared(
|
|||||||
/**
|
/**
|
||||||
* Optimize expression without derivatives
|
* Optimize expression without derivatives
|
||||||
*/
|
*/
|
||||||
public fun Expression<Double>.optimize(
|
public suspend fun Expression<Double>.optimize(
|
||||||
vararg symbols: Symbol,
|
vararg symbols: Symbol,
|
||||||
configuration: CMOptimization.() -> Unit,
|
configuration: CMOptimization.() -> Unit,
|
||||||
): OptimizationResult<Double> = noDerivOptimizeWith(CMOptimization, symbols = symbols, configuration)
|
): OptimizationResult<Double> {
|
||||||
|
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
|
* Optimize differentiable expression
|
||||||
*/
|
*/
|
||||||
public fun DifferentiableExpression<Double, Expression<Double>>.optimize(
|
public suspend fun DifferentiableExpression<Double, Expression<Double>>.optimize(
|
||||||
vararg symbols: Symbol,
|
vararg symbols: Symbol,
|
||||||
configuration: CMOptimization.() -> Unit,
|
configuration: CMOptimization.() -> Unit,
|
||||||
): OptimizationResult<Double> = optimizeWith(CMOptimization, symbols = symbols, configuration)
|
): OptimizationResult<Double> = optimizeWith(CMOptimization, symbols = symbols, configuration)
|
||||||
|
|
||||||
public fun DifferentiableExpression<Double, Expression<Double>>.minimize(
|
public suspend fun DifferentiableExpression<Double, Expression<Double>>.minimize(
|
||||||
vararg startPoint: Pair<Symbol, Double>,
|
vararg startPoint: Pair<Symbol, Double>,
|
||||||
configuration: CMOptimization.() -> Unit = {},
|
configuration: CMOptimization.() -> Unit = {},
|
||||||
): OptimizationResult<Double> {
|
): OptimizationResult<Double> {
|
||||||
@ -67,7 +69,7 @@ public fun DifferentiableExpression<Double, Expression<Double>>.minimize(
|
|||||||
return optimize(*symbols){
|
return optimize(*symbols){
|
||||||
maximize = false
|
maximize = false
|
||||||
initialGuess(startPoint.toMap())
|
initialGuess(startPoint.toMap())
|
||||||
diffFunction(this@minimize)
|
function(this@minimize)
|
||||||
configuration()
|
configuration()
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -24,7 +24,7 @@ internal class OptimizeTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testGradientOptimization() {
|
fun testGradientOptimization() = runBlocking{
|
||||||
val result = normal.optimize(x, y) {
|
val result = normal.optimize(x, y) {
|
||||||
initialGuess(x to 1.0, y to 1.0)
|
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
|
//no need to select optimizer. Gradient optimizer is used by default because gradients are provided by function
|
||||||
@ -34,7 +34,7 @@ internal class OptimizeTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testSimplexOptimization() {
|
fun testSimplexOptimization() = runBlocking{
|
||||||
val result = normal.optimize(x, y) {
|
val result = normal.optimize(x, y) {
|
||||||
initialGuess(x to 1.0, y to 1.0)
|
initialGuess(x to 1.0, y to 1.0)
|
||||||
simplexSteps(x to 2.0, y to 0.5)
|
simplexSteps(x to 2.0, y to 0.5)
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* 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.data
|
||||||
|
|
||||||
|
import space.kscience.kmath.misc.Symbol
|
||||||
|
import space.kscience.kmath.misc.Symbol.Companion.z
|
||||||
|
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||||
|
import space.kscience.kmath.misc.symbol
|
||||||
|
import space.kscience.kmath.structures.Buffer
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [ColumnarData] with additional [Companion.yErr] column for an [Symbol.y] error
|
||||||
|
* Inherits [XYColumnarData].
|
||||||
|
*/
|
||||||
|
@UnstableKMathAPI
|
||||||
|
public interface XYErrorColumnarData<T, out X : T, out Y : T> : XYColumnarData<T, X, Y> {
|
||||||
|
public val yErr: Buffer<Y>
|
||||||
|
|
||||||
|
override fun get(symbol: Symbol): Buffer<T> = when (symbol) {
|
||||||
|
Symbol.x -> x
|
||||||
|
Symbol.y -> y
|
||||||
|
Companion.yErr -> yErr
|
||||||
|
else -> error("A column for symbol $symbol not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
public companion object{
|
||||||
|
public val yErr: Symbol by symbol
|
||||||
|
}
|
||||||
|
}
|
@ -10,7 +10,7 @@ import space.kscience.kmath.misc.UnstableKMathAPI
|
|||||||
import space.kscience.kmath.structures.Buffer
|
import space.kscience.kmath.structures.Buffer
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A [XYColumnarData] with guaranteed [x], [y] and [z] columns designated by corresponding symbols.
|
* A [ColumnarData] with guaranteed [x], [y] and [z] columns designated by corresponding symbols.
|
||||||
* Inherits [XYColumnarData].
|
* Inherits [XYColumnarData].
|
||||||
*/
|
*/
|
||||||
@UnstableKMathAPI
|
@UnstableKMathAPI
|
||||||
|
@ -51,6 +51,6 @@ public abstract class FirstDerivativeExpression<T, R : Expression<T>> : Differen
|
|||||||
/**
|
/**
|
||||||
* A factory that converts an expression in autodiff variables to a [DifferentiableExpression]
|
* A factory that converts an expression in autodiff variables to a [DifferentiableExpression]
|
||||||
*/
|
*/
|
||||||
public fun interface AutoDiffProcessor<T : Any, I : Any, A : ExpressionAlgebra<T, I>, out R : Expression<T>> {
|
public fun interface AutoDiffProcessor<T, I, A : ExpressionAlgebra<T, I>, out R : Expression<T>> {
|
||||||
public fun process(function: A.() -> I): DifferentiableExpression<T, R>
|
public fun process(function: A.() -> I): DifferentiableExpression<T, R>
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import space.kscience.kmath.misc.Symbol
|
|||||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||||
import space.kscience.kmath.nd.Structure2D
|
import space.kscience.kmath.nd.Structure2D
|
||||||
import space.kscience.kmath.structures.BufferFactory
|
import space.kscience.kmath.structures.BufferFactory
|
||||||
|
import space.kscience.kmath.structures.DoubleBuffer
|
||||||
import kotlin.jvm.JvmInline
|
import kotlin.jvm.JvmInline
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -46,6 +47,11 @@ public interface SymbolIndexer {
|
|||||||
return symbols.indices.associate { symbols[it] to get(it) }
|
return symbols.indices.associate { symbols[it] to get(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public fun <T> Point<T>.toMap(): Map<Symbol, T> {
|
||||||
|
require(size == symbols.size) { "The input array size for indexer should be ${symbols.size} but $size found" }
|
||||||
|
return symbols.indices.associate { symbols[it] to get(it) }
|
||||||
|
}
|
||||||
|
|
||||||
public operator fun <T> Structure2D<T>.get(rowSymbol: Symbol, columnSymbol: Symbol): T =
|
public operator fun <T> Structure2D<T>.get(rowSymbol: Symbol, columnSymbol: Symbol): T =
|
||||||
get(indexOf(rowSymbol), indexOf(columnSymbol))
|
get(indexOf(rowSymbol), indexOf(columnSymbol))
|
||||||
|
|
||||||
@ -55,6 +61,10 @@ public interface SymbolIndexer {
|
|||||||
public fun <T> Map<Symbol, T>.toPoint(bufferFactory: BufferFactory<T>): Point<T> =
|
public fun <T> Map<Symbol, T>.toPoint(bufferFactory: BufferFactory<T>): Point<T> =
|
||||||
bufferFactory(symbols.size) { getValue(symbols[it]) }
|
bufferFactory(symbols.size) { getValue(symbols[it]) }
|
||||||
|
|
||||||
|
public fun Map<Symbol, Double>.toPoint(): DoubleBuffer =
|
||||||
|
DoubleBuffer(symbols.size) { getValue(symbols[it]) }
|
||||||
|
|
||||||
|
|
||||||
public fun Map<Symbol, Double>.toDoubleArray(): DoubleArray = DoubleArray(symbols.size) { getValue(symbols[it]) }
|
public fun Map<Symbol, Double>.toDoubleArray(): DoubleArray = DoubleArray(symbols.size) { getValue(symbols[it]) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* 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 <T : Any, LS : LinearSpace<T, *>> LS.buildSymmetricMatrix(
|
||||||
|
size: Int,
|
||||||
|
builder: (i: Int, j: Int) -> T,
|
||||||
|
): Matrix<T> = BufferAccessor2D<T?>(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
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* 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.misc
|
||||||
|
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A entity that contains a set of features defined by their types
|
||||||
|
*/
|
||||||
|
public interface Featured<F : Any> {
|
||||||
|
public fun <T : F> getFeature(type: KClass<out T>): T?
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A container for a set of features
|
||||||
|
*/
|
||||||
|
public class FeatureSet<F : Any> private constructor(public val features: Map<KClass<out F>, Any>) : Featured<F> {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
override fun <T : F> getFeature(type: KClass<out T>): T? = features[type] as? T
|
||||||
|
|
||||||
|
public inline fun <reified T : F> getFeature(): T? = getFeature(T::class)
|
||||||
|
|
||||||
|
public fun <T : F> with(feature: T, type: KClass<out T> = feature::class): FeatureSet<F> =
|
||||||
|
FeatureSet(features + (type to feature))
|
||||||
|
|
||||||
|
public fun with(other: FeatureSet<F>): FeatureSet<F> = FeatureSet(features + other.features)
|
||||||
|
|
||||||
|
public fun with(vararg otherFeatures: F): FeatureSet<F> =
|
||||||
|
FeatureSet(features + otherFeatures.associateBy { it::class })
|
||||||
|
|
||||||
|
public companion object {
|
||||||
|
public fun <F : Any> of(vararg features: F): FeatureSet<F> = FeatureSet(features.associateBy { it::class })
|
||||||
|
}
|
||||||
|
}
|
@ -13,7 +13,7 @@ import space.kscience.kmath.nd.as2D
|
|||||||
/**
|
/**
|
||||||
* A context that allows to operate on a [MutableBuffer] as on 2d array
|
* A context that allows to operate on a [MutableBuffer] as on 2d array
|
||||||
*/
|
*/
|
||||||
internal class BufferAccessor2D<T : Any>(
|
internal class BufferAccessor2D<T>(
|
||||||
public val rowNum: Int,
|
public val rowNum: Int,
|
||||||
public val colNum: Int,
|
public val colNum: Int,
|
||||||
val factory: MutableBufferFactory<T>,
|
val factory: MutableBufferFactory<T>,
|
||||||
|
@ -5,8 +5,10 @@
|
|||||||
|
|
||||||
package space.kscience.kmath.structures
|
package space.kscience.kmath.structures
|
||||||
|
|
||||||
|
import space.kscience.kmath.linear.Point
|
||||||
import space.kscience.kmath.operations.ExtendedField
|
import space.kscience.kmath.operations.ExtendedField
|
||||||
import space.kscience.kmath.operations.ExtendedFieldOperations
|
import space.kscience.kmath.operations.ExtendedFieldOperations
|
||||||
|
import space.kscience.kmath.operations.Norm
|
||||||
import kotlin.math.*
|
import kotlin.math.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -161,12 +163,16 @@ public object DoubleBufferFieldOperations : ExtendedFieldOperations<Buffer<Doubl
|
|||||||
DoubleBuffer(DoubleArray(arg.size) { ln(arg[it]) })
|
DoubleBuffer(DoubleArray(arg.size) { ln(arg[it]) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public object DoubleL2Norm : Norm<Point<Double>, Double> {
|
||||||
|
override fun norm(arg: Point<Double>): Double = sqrt(arg.fold(0.0) { acc: Double, d: Double -> acc + d.pow(2) })
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [ExtendedField] over [DoubleBuffer].
|
* [ExtendedField] over [DoubleBuffer].
|
||||||
*
|
*
|
||||||
* @property size the size of buffers to operate on.
|
* @property size the size of buffers to operate on.
|
||||||
*/
|
*/
|
||||||
public class DoubleBufferField(public val size: Int) : ExtendedField<Buffer<Double>> {
|
public class DoubleBufferField(public val size: Int) : ExtendedField<Buffer<Double>>, Norm<Buffer<Double>, Double> {
|
||||||
public override val zero: Buffer<Double> by lazy { DoubleBuffer(size) { 0.0 } }
|
public override val zero: Buffer<Double> by lazy { DoubleBuffer(size) { 0.0 } }
|
||||||
public override val one: Buffer<Double> by lazy { DoubleBuffer(size) { 1.0 } }
|
public override val one: Buffer<Double> by lazy { DoubleBuffer(size) { 1.0 } }
|
||||||
|
|
||||||
@ -274,4 +280,6 @@ public class DoubleBufferField(public val size: Int) : ExtendedField<Buffer<Doub
|
|||||||
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
|
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
|
||||||
return DoubleBufferFieldOperations.ln(arg)
|
return DoubleBufferFieldOperations.ln(arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun norm(arg: Buffer<Double>): Double = DoubleL2Norm.norm(arg)
|
||||||
}
|
}
|
||||||
|
@ -8,11 +8,8 @@ package space.kscience.kmath.real
|
|||||||
import space.kscience.kmath.linear.Point
|
import space.kscience.kmath.linear.Point
|
||||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||||
import space.kscience.kmath.operations.Norm
|
import space.kscience.kmath.operations.Norm
|
||||||
import space.kscience.kmath.structures.Buffer
|
import space.kscience.kmath.structures.*
|
||||||
import space.kscience.kmath.structures.MutableBuffer.Companion.double
|
import space.kscience.kmath.structures.MutableBuffer.Companion.double
|
||||||
import space.kscience.kmath.structures.asBuffer
|
|
||||||
import space.kscience.kmath.structures.fold
|
|
||||||
import space.kscience.kmath.structures.indices
|
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
import kotlin.math.sqrt
|
import kotlin.math.sqrt
|
||||||
|
|
||||||
@ -105,8 +102,4 @@ public fun DoubleVector.sum(): Double {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
public object VectorL2Norm : Norm<DoubleVector, Double> {
|
public val DoubleVector.norm: Double get() = DoubleL2Norm.norm(this)
|
||||||
override fun norm(arg: DoubleVector): Double = sqrt(arg.fold(0.0) { acc: Double, d: Double -> acc + d.pow(2) })
|
|
||||||
}
|
|
||||||
|
|
||||||
public val DoubleVector.norm: Double get() = VectorL2Norm.norm(this)
|
|
@ -50,7 +50,7 @@ public class GaussIntegrator<T : Any>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun integrate(integrand: UnivariateIntegrand<T>): UnivariateIntegrand<T> = with(algebra) {
|
override fun process(integrand: UnivariateIntegrand<T>): UnivariateIntegrand<T> = with(algebra) {
|
||||||
val f = integrand.function
|
val f = integrand.function
|
||||||
val (points, weights) = buildRule(integrand)
|
val (points, weights) = buildRule(integrand)
|
||||||
var res = zero
|
var res = zero
|
||||||
@ -63,7 +63,7 @@ public class GaussIntegrator<T : Any>(
|
|||||||
c = t - res - y
|
c = t - res - y
|
||||||
res = t
|
res = t
|
||||||
}
|
}
|
||||||
return integrand + IntegrandValue(res) + IntegrandCallsPerformed(integrand.calls + points.size)
|
return integrand.with(IntegrandValue(res),IntegrandCallsPerformed(integrand.calls + points.size))
|
||||||
}
|
}
|
||||||
|
|
||||||
public companion object {
|
public companion object {
|
||||||
@ -80,17 +80,17 @@ public class GaussIntegrator<T : Any>(
|
|||||||
* * [UnivariateIntegrandRanges] - Set of ranges and number of points per range. Defaults to given [IntegrationRange] and [IntegrandMaxCalls]
|
* * [UnivariateIntegrandRanges] - Set of ranges and number of points per range. Defaults to given [IntegrationRange] and [IntegrandMaxCalls]
|
||||||
*/
|
*/
|
||||||
@UnstableKMathAPI
|
@UnstableKMathAPI
|
||||||
public fun <T : Any> Field<T>.integrate(
|
public fun <T : Any> Field<T>.process(
|
||||||
vararg features: IntegrandFeature,
|
vararg features: IntegrandFeature,
|
||||||
function: (Double) -> T,
|
function: (Double) -> T,
|
||||||
): UnivariateIntegrand<T> = GaussIntegrator(this).integrate(UnivariateIntegrand(function, *features))
|
): UnivariateIntegrand<T> = GaussIntegrator(this).process(UnivariateIntegrand(function, *features))
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use [GaussIntegrator.Companion.integrate] to integrate the function in the current algebra with given [range] and [numPoints]
|
* Use [GaussIntegrator.Companion.integrate] to integrate the function in the current algebra with given [range] and [numPoints]
|
||||||
*/
|
*/
|
||||||
@UnstableKMathAPI
|
@UnstableKMathAPI
|
||||||
public fun <T : Any> Field<T>.integrate(
|
public fun <T : Any> Field<T>.process(
|
||||||
range: ClosedRange<Double>,
|
range: ClosedRange<Double>,
|
||||||
order: Int = 10,
|
order: Int = 10,
|
||||||
intervals: Int = 10,
|
intervals: Int = 10,
|
||||||
@ -104,7 +104,7 @@ public fun <T : Any> Field<T>.integrate(
|
|||||||
val ranges = UnivariateIntegrandRanges(
|
val ranges = UnivariateIntegrandRanges(
|
||||||
(0 until intervals).map { i -> (rangeSize * i)..(rangeSize * (i + 1)) to order }
|
(0 until intervals).map { i -> (rangeSize * i)..(rangeSize * (i + 1)) to order }
|
||||||
)
|
)
|
||||||
return GaussIntegrator(this).integrate(
|
return GaussIntegrator(this).process(
|
||||||
UnivariateIntegrand(
|
UnivariateIntegrand(
|
||||||
function,
|
function,
|
||||||
IntegrationRange(range),
|
IntegrationRange(range),
|
||||||
|
@ -5,12 +5,15 @@
|
|||||||
|
|
||||||
package space.kscience.kmath.integration
|
package space.kscience.kmath.integration
|
||||||
|
|
||||||
|
import space.kscience.kmath.misc.FeatureSet
|
||||||
|
import space.kscience.kmath.misc.Featured
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
public interface IntegrandFeature
|
public interface IntegrandFeature
|
||||||
|
|
||||||
public interface Integrand {
|
public interface Integrand: Featured<IntegrandFeature>{
|
||||||
public fun <T : IntegrandFeature> getFeature(type: KClass<T>): T?
|
public val features: FeatureSet<IntegrandFeature>
|
||||||
|
override fun <T : IntegrandFeature> getFeature(type: KClass<out T>): T? = features.getFeature(type)
|
||||||
}
|
}
|
||||||
|
|
||||||
public inline fun <reified T : IntegrandFeature> Integrand.getFeature(): T? = getFeature(T::class)
|
public inline fun <reified T : IntegrandFeature> Integrand.getFeature(): T? = getFeature(T::class)
|
||||||
|
@ -12,5 +12,5 @@ public interface Integrator<I : Integrand> {
|
|||||||
/**
|
/**
|
||||||
* Runs one integration pass and return a new [Integrand] with a new set of features.
|
* Runs one integration pass and return a new [Integrand] with a new set of features.
|
||||||
*/
|
*/
|
||||||
public fun integrate(integrand: I): I
|
public fun process(integrand: I): I
|
||||||
}
|
}
|
||||||
|
@ -6,27 +6,21 @@
|
|||||||
package space.kscience.kmath.integration
|
package space.kscience.kmath.integration
|
||||||
|
|
||||||
import space.kscience.kmath.linear.Point
|
import space.kscience.kmath.linear.Point
|
||||||
|
import space.kscience.kmath.misc.FeatureSet
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
public class MultivariateIntegrand<T : Any> internal constructor(
|
public class MultivariateIntegrand<T : Any> internal constructor(
|
||||||
private val features: Map<KClass<*>, IntegrandFeature>,
|
override val features: FeatureSet<IntegrandFeature>,
|
||||||
public val function: (Point<T>) -> T,
|
public val function: (Point<T>) -> T,
|
||||||
) : Integrand {
|
) : Integrand
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
public fun <T : Any> MultivariateIntegrand<T>.with(vararg newFeatures: IntegrandFeature): MultivariateIntegrand<T> =
|
||||||
override fun <T : IntegrandFeature> getFeature(type: KClass<T>): T? = features[type] as? T
|
MultivariateIntegrand(features.with(*newFeatures), function)
|
||||||
|
|
||||||
public operator fun <F : IntegrandFeature> plus(pair: Pair<KClass<out F>, F>): MultivariateIntegrand<T> =
|
|
||||||
MultivariateIntegrand(features + pair, function)
|
|
||||||
|
|
||||||
public operator fun <F : IntegrandFeature> plus(feature: F): MultivariateIntegrand<T> =
|
|
||||||
plus(feature::class to feature)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("FunctionName")
|
@Suppress("FunctionName")
|
||||||
public fun <T : Any> MultivariateIntegrand(
|
public fun <T : Any> MultivariateIntegrand(
|
||||||
vararg features: IntegrandFeature,
|
vararg features: IntegrandFeature,
|
||||||
function: (Point<T>) -> T,
|
function: (Point<T>) -> T,
|
||||||
): MultivariateIntegrand<T> = MultivariateIntegrand(features.associateBy { it::class }, function)
|
): MultivariateIntegrand<T> = MultivariateIntegrand(FeatureSet.of(*features), function)
|
||||||
|
|
||||||
public val <T : Any> MultivariateIntegrand<T>.value: T? get() = getFeature<IntegrandValue<T>>()?.value
|
public val <T : Any> MultivariateIntegrand<T>.value: T? get() = getFeature<IntegrandValue<T>>()?.value
|
||||||
|
@ -5,30 +5,24 @@
|
|||||||
|
|
||||||
package space.kscience.kmath.integration
|
package space.kscience.kmath.integration
|
||||||
|
|
||||||
|
import space.kscience.kmath.misc.FeatureSet
|
||||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||||
import kotlin.jvm.JvmInline
|
import kotlin.jvm.JvmInline
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
public class UnivariateIntegrand<T : Any> internal constructor(
|
public class UnivariateIntegrand<T : Any> internal constructor(
|
||||||
private val features: Map<KClass<*>, IntegrandFeature>,
|
override val features: FeatureSet<IntegrandFeature>,
|
||||||
public val function: (Double) -> T,
|
public val function: (Double) -> T,
|
||||||
) : Integrand {
|
) : Integrand
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
public fun <T : Any> UnivariateIntegrand<T>.with(vararg newFeatures: IntegrandFeature): UnivariateIntegrand<T> =
|
||||||
override fun <T : IntegrandFeature> getFeature(type: KClass<T>): T? = features[type] as? T
|
UnivariateIntegrand(features.with(*newFeatures), function)
|
||||||
|
|
||||||
public operator fun <F : IntegrandFeature> plus(pair: Pair<KClass<out F>, F>): UnivariateIntegrand<T> =
|
|
||||||
UnivariateIntegrand(features + pair, function)
|
|
||||||
|
|
||||||
public operator fun <F : IntegrandFeature> plus(feature: F): UnivariateIntegrand<T> =
|
|
||||||
plus(feature::class to feature)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("FunctionName")
|
@Suppress("FunctionName")
|
||||||
public fun <T : Any> UnivariateIntegrand(
|
public fun <T : Any> UnivariateIntegrand(
|
||||||
function: (Double) -> T,
|
function: (Double) -> T,
|
||||||
vararg features: IntegrandFeature,
|
vararg features: IntegrandFeature,
|
||||||
): UnivariateIntegrand<T> = UnivariateIntegrand(features.associateBy { it::class }, function)
|
): UnivariateIntegrand<T> = UnivariateIntegrand(FeatureSet.of(*features), function)
|
||||||
|
|
||||||
public typealias UnivariateIntegrator<T> = Integrator<UnivariateIntegrand<T>>
|
public typealias UnivariateIntegrator<T> = Integrator<UnivariateIntegrand<T>>
|
||||||
|
|
||||||
@ -46,7 +40,7 @@ public fun UnivariateIntegrator<Double>.integrate(
|
|||||||
range: ClosedRange<Double>,
|
range: ClosedRange<Double>,
|
||||||
vararg features: IntegrandFeature,
|
vararg features: IntegrandFeature,
|
||||||
function: (Double) -> Double,
|
function: (Double) -> Double,
|
||||||
): Double = integrate(
|
): Double = process(
|
||||||
UnivariateIntegrand(function, IntegrationRange(range), *features)
|
UnivariateIntegrand(function, IntegrationRange(range), *features)
|
||||||
).value ?: error("Unexpected: no value after integration.")
|
).value ?: error("Unexpected: no value after integration.")
|
||||||
|
|
||||||
@ -65,7 +59,7 @@ public fun UnivariateIntegrator<Double>.integrate(
|
|||||||
featureBuilder()
|
featureBuilder()
|
||||||
add(IntegrationRange(range))
|
add(IntegrationRange(range))
|
||||||
}
|
}
|
||||||
return integrate(
|
return process(
|
||||||
UnivariateIntegrand(function, *features.toTypedArray())
|
UnivariateIntegrand(function, *features.toTypedArray())
|
||||||
).value ?: error("Unexpected: no value after integration.")
|
).value ?: error("Unexpected: no value after integration.")
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ import kotlin.test.assertEquals
|
|||||||
class GaussIntegralTest {
|
class GaussIntegralTest {
|
||||||
@Test
|
@Test
|
||||||
fun gaussSin() {
|
fun gaussSin() {
|
||||||
val res = DoubleField.integrate(0.0..2 * PI) { x ->
|
val res = DoubleField.process(0.0..2 * PI) { x ->
|
||||||
sin(x)
|
sin(x)
|
||||||
}
|
}
|
||||||
assertEquals(0.0, res.value!!, 1e-2)
|
assertEquals(0.0, res.value!!, 1e-2)
|
||||||
@ -24,7 +24,7 @@ class GaussIntegralTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun gaussUniform() {
|
fun gaussUniform() {
|
||||||
val res = DoubleField.integrate(0.0..100.0) { x ->
|
val res = DoubleField.process(0.0..100.0) { x ->
|
||||||
if(x in 30.0..50.0){
|
if(x in 30.0..50.0){
|
||||||
1.0
|
1.0
|
||||||
} else {
|
} else {
|
||||||
|
@ -9,86 +9,97 @@ import space.kscience.kmath.expressions.AutoDiffProcessor
|
|||||||
import space.kscience.kmath.expressions.DifferentiableExpression
|
import space.kscience.kmath.expressions.DifferentiableExpression
|
||||||
import space.kscience.kmath.expressions.Expression
|
import space.kscience.kmath.expressions.Expression
|
||||||
import space.kscience.kmath.expressions.ExpressionAlgebra
|
import space.kscience.kmath.expressions.ExpressionAlgebra
|
||||||
|
import space.kscience.kmath.misc.FeatureSet
|
||||||
import space.kscience.kmath.misc.Symbol
|
import space.kscience.kmath.misc.Symbol
|
||||||
import space.kscience.kmath.operations.ExtendedField
|
import space.kscience.kmath.operations.ExtendedField
|
||||||
import space.kscience.kmath.structures.Buffer
|
import space.kscience.kmath.structures.Buffer
|
||||||
import space.kscience.kmath.structures.indices
|
import space.kscience.kmath.structures.indices
|
||||||
|
|
||||||
/**
|
|
||||||
* A likelihood function optimization problem with provided derivatives
|
|
||||||
*/
|
|
||||||
public interface FunctionOptimization<T : Any> : Optimization<T> {
|
|
||||||
/**
|
|
||||||
* The optimization direction. If true search for function maximum, if false, search for the minimum
|
|
||||||
*/
|
|
||||||
public var maximize: Boolean
|
|
||||||
|
|
||||||
/**
|
public class FunctionOptimization<T : Any>(
|
||||||
* Define the initial guess for the optimization problem
|
override val features: FeatureSet<OptimizationFeature>,
|
||||||
*/
|
public val expression: DifferentiableExpression<T, Expression<T>>,
|
||||||
public fun initialGuess(map: Map<Symbol, T>)
|
public val initialGuess: Map<Symbol, T>,
|
||||||
|
public val parameters: Collection<Symbol>,
|
||||||
|
public val maximize: Boolean,
|
||||||
|
) : OptimizationProblem
|
||||||
|
|
||||||
/**
|
//
|
||||||
* Set a differentiable expression as objective function as function and gradient provider
|
///**
|
||||||
*/
|
// * A likelihood function optimization problem with provided derivatives
|
||||||
public fun diffFunction(expression: DifferentiableExpression<T, Expression<T>>)
|
// */
|
||||||
|
//public interface FunctionOptimizationBuilder<T : Any> {
|
||||||
public companion object {
|
// /**
|
||||||
/**
|
// * The optimization direction. If true search for function maximum, if false, search for the minimum
|
||||||
* Generate a chi squared expression from given x-y-sigma data and inline model. Provides automatic differentiation
|
// */
|
||||||
*/
|
// public var maximize: Boolean
|
||||||
public fun <T : Any, I : Any, A> chiSquared(
|
//
|
||||||
autoDiff: AutoDiffProcessor<T, I, A, Expression<T>>,
|
// /**
|
||||||
x: Buffer<T>,
|
// * Define the initial guess for the optimization problem
|
||||||
y: Buffer<T>,
|
// */
|
||||||
yErr: Buffer<T>,
|
// public fun initialGuess(map: Map<Symbol, T>)
|
||||||
model: A.(I) -> I,
|
//
|
||||||
): DifferentiableExpression<T, Expression<T>> where A : ExtendedField<I>, A : ExpressionAlgebra<T, I> {
|
// /**
|
||||||
require(x.size == y.size) { "X and y buffers should be of the same size" }
|
// * Set a differentiable expression as objective function as function and gradient provider
|
||||||
require(y.size == yErr.size) { "Y and yErr buffer should of the same size" }
|
// */
|
||||||
|
// public fun function(expression: DifferentiableExpression<T, Expression<T>>)
|
||||||
return autoDiff.process {
|
//
|
||||||
var sum = zero
|
// public companion object {
|
||||||
|
// /**
|
||||||
x.indices.forEach {
|
// * Generate a chi squared expression from given x-y-sigma data and inline model. Provides automatic differentiation
|
||||||
val xValue = const(x[it])
|
// */
|
||||||
val yValue = const(y[it])
|
// public fun <T : Any, I : Any, A> chiSquared(
|
||||||
val yErrValue = const(yErr[it])
|
// autoDiff: AutoDiffProcessor<T, I, A, Expression<T>>,
|
||||||
val modelValue = model(xValue)
|
// x: Buffer<T>,
|
||||||
sum += ((yValue - modelValue) / yErrValue).pow(2)
|
// y: Buffer<T>,
|
||||||
}
|
// yErr: Buffer<T>,
|
||||||
|
// model: A.(I) -> I,
|
||||||
sum
|
// ): DifferentiableExpression<T, Expression<T>> where A : ExtendedField<I>, A : ExpressionAlgebra<T, I> {
|
||||||
}
|
// require(x.size == y.size) { "X and y buffers should be of the same size" }
|
||||||
}
|
// require(y.size == yErr.size) { "Y and yErr buffer should of the same size" }
|
||||||
}
|
//
|
||||||
}
|
// return autoDiff.process {
|
||||||
|
// var sum = zero
|
||||||
/**
|
//
|
||||||
* Define a chi-squared-based objective function
|
// x.indices.forEach {
|
||||||
*/
|
// val xValue = const(x[it])
|
||||||
public fun <T: Any, I : Any, A> FunctionOptimization<T>.chiSquared(
|
// val yValue = const(y[it])
|
||||||
autoDiff: AutoDiffProcessor<T, I, A, Expression<T>>,
|
// val yErrValue = const(yErr[it])
|
||||||
x: Buffer<T>,
|
// val modelValue = model(xValue)
|
||||||
y: Buffer<T>,
|
// sum += ((yValue - modelValue) / yErrValue).pow(2)
|
||||||
yErr: Buffer<T>,
|
// }
|
||||||
model: A.(I) -> I,
|
//
|
||||||
) where A : ExtendedField<I>, A : ExpressionAlgebra<T, I> {
|
// sum
|
||||||
val chiSquared = FunctionOptimization.chiSquared(autoDiff, x, y, yErr, model)
|
// }
|
||||||
diffFunction(chiSquared)
|
// }
|
||||||
maximize = false
|
// }
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
/**
|
///**
|
||||||
* Optimize differentiable expression using specific [OptimizationProblemFactory]
|
// * Define a chi-squared-based objective function
|
||||||
*/
|
// */
|
||||||
public fun <T : Any, F : FunctionOptimization<T>> DifferentiableExpression<T, Expression<T>>.optimizeWith(
|
//public fun <T : Any, I : Any, A> FunctionOptimization<T>.chiSquared(
|
||||||
factory: OptimizationProblemFactory<T, F>,
|
// autoDiff: AutoDiffProcessor<T, I, A, Expression<T>>,
|
||||||
vararg symbols: Symbol,
|
// x: Buffer<T>,
|
||||||
configuration: F.() -> Unit,
|
// y: Buffer<T>,
|
||||||
): OptimizationResult<T> {
|
// yErr: Buffer<T>,
|
||||||
require(symbols.isNotEmpty()) { "Must provide a list of symbols for optimization" }
|
// model: A.(I) -> I,
|
||||||
val problem = factory(symbols.toList(), configuration)
|
//) where A : ExtendedField<I>, A : ExpressionAlgebra<T, I> {
|
||||||
problem.diffFunction(this)
|
// val chiSquared = FunctionOptimization.chiSquared(autoDiff, x, y, yErr, model)
|
||||||
return problem.optimize()
|
// function(chiSquared)
|
||||||
}
|
// maximize = false
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
///**
|
||||||
|
// * Optimize differentiable expression using specific [OptimizationProblemFactory]
|
||||||
|
// */
|
||||||
|
//public suspend fun <T : Any, F : FunctionOptimization<T>> DifferentiableExpression<T, Expression<T>>.optimizeWith(
|
||||||
|
// factory: OptimizationProblemFactory<T, F>,
|
||||||
|
// vararg symbols: Symbol,
|
||||||
|
// configuration: F.() -> Unit,
|
||||||
|
//): OptimizationResult<T> {
|
||||||
|
// require(symbols.isNotEmpty()) { "Must provide a list of symbols for optimization" }
|
||||||
|
// val problem = factory(symbols.toList(), configuration)
|
||||||
|
// problem.function(this)
|
||||||
|
// return problem.optimize()
|
||||||
|
//}
|
||||||
|
@ -1,74 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2018-2021 KMath contributors.
|
|
||||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package space.kscience.kmath.optimization
|
|
||||||
|
|
||||||
import space.kscience.kmath.expressions.Expression
|
|
||||||
import space.kscience.kmath.misc.Symbol
|
|
||||||
import space.kscience.kmath.structures.Buffer
|
|
||||||
import space.kscience.kmath.structures.indices
|
|
||||||
import kotlin.math.pow
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A likelihood function optimization problem
|
|
||||||
*/
|
|
||||||
public interface NoDerivFunctionOptimization<T : Any> : Optimization<T> {
|
|
||||||
/**
|
|
||||||
* The optimization direction. If true search for function maximum, if false, search for the minimum
|
|
||||||
*/
|
|
||||||
public var maximize: Boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define the initial guess for the optimization problem
|
|
||||||
*/
|
|
||||||
public fun initialGuess(map: Map<Symbol, T>)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set an objective function expression
|
|
||||||
*/
|
|
||||||
public fun function(expression: Expression<T>)
|
|
||||||
|
|
||||||
public companion object {
|
|
||||||
/**
|
|
||||||
* Generate a chi squared expression from given x-y-sigma model represented by an expression. Does not provide derivatives
|
|
||||||
*/
|
|
||||||
public fun chiSquared(
|
|
||||||
x: Buffer<Double>,
|
|
||||||
y: Buffer<Double>,
|
|
||||||
yErr: Buffer<Double>,
|
|
||||||
model: Expression<Double>,
|
|
||||||
xSymbol: Symbol = Symbol.x,
|
|
||||||
): Expression<Double> {
|
|
||||||
require(x.size == y.size) { "X and y buffers should be of the same size" }
|
|
||||||
require(y.size == yErr.size) { "Y and yErr buffer should of the same size" }
|
|
||||||
|
|
||||||
return Expression { arguments ->
|
|
||||||
x.indices.sumOf {
|
|
||||||
val xValue = x[it]
|
|
||||||
val yValue = y[it]
|
|
||||||
val yErrValue = yErr[it]
|
|
||||||
val modifiedArgs = arguments + (xSymbol to xValue)
|
|
||||||
val modelValue = model(modifiedArgs)
|
|
||||||
((yValue - modelValue) / yErrValue).pow(2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Optimize expression without derivatives using specific [OptimizationProblemFactory]
|
|
||||||
*/
|
|
||||||
public fun <T : Any, F : NoDerivFunctionOptimization<T>> Expression<T>.noDerivOptimizeWith(
|
|
||||||
factory: OptimizationProblemFactory<T, F>,
|
|
||||||
vararg symbols: Symbol,
|
|
||||||
configuration: F.() -> Unit,
|
|
||||||
): OptimizationResult<T> {
|
|
||||||
require(symbols.isNotEmpty()) { "Must provide a list of symbols for optimization" }
|
|
||||||
val problem = factory(symbols.toList(), configuration)
|
|
||||||
problem.function(this)
|
|
||||||
return problem.optimize()
|
|
||||||
}
|
|
@ -1,49 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2018-2021 KMath contributors.
|
|
||||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package space.kscience.kmath.optimization
|
|
||||||
|
|
||||||
import space.kscience.kmath.misc.Symbol
|
|
||||||
|
|
||||||
public interface OptimizationFeature
|
|
||||||
|
|
||||||
public class OptimizationResult<T>(
|
|
||||||
public val point: Map<Symbol, T>,
|
|
||||||
public val value: T,
|
|
||||||
public val features: Set<OptimizationFeature> = emptySet(),
|
|
||||||
) {
|
|
||||||
override fun toString(): String {
|
|
||||||
return "OptimizationResult(point=$point, value=$value)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public operator fun <T> OptimizationResult<T>.plus(
|
|
||||||
feature: OptimizationFeature,
|
|
||||||
): OptimizationResult<T> = OptimizationResult(point, value, features + feature)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An optimization problem builder over [T] variables
|
|
||||||
*/
|
|
||||||
public interface Optimization<T : Any> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the problem from previous optimization run
|
|
||||||
*/
|
|
||||||
public fun update(result: OptimizationResult<T>)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Make an optimization run
|
|
||||||
*/
|
|
||||||
public fun optimize(): OptimizationResult<T>
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun interface OptimizationProblemFactory<T : Any, out P : Optimization<T>> {
|
|
||||||
public fun build(symbols: List<Symbol>): P
|
|
||||||
}
|
|
||||||
|
|
||||||
public operator fun <T : Any, P : Optimization<T>> OptimizationProblemFactory<T, P>.invoke(
|
|
||||||
symbols: List<Symbol>,
|
|
||||||
block: P.() -> Unit,
|
|
||||||
): P = build(symbols).apply(block)
|
|
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* 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.misc.FeatureSet
|
||||||
|
import space.kscience.kmath.misc.Featured
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
public interface OptimizationFeature
|
||||||
|
|
||||||
|
public interface OptimizationProblem : Featured<OptimizationFeature> {
|
||||||
|
public val features: FeatureSet<OptimizationFeature>
|
||||||
|
override fun <T : OptimizationFeature> getFeature(type: KClass<out T>): T? = features.getFeature(type)
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline fun <reified T : OptimizationFeature> OptimizationProblem.getFeature(): T? = getFeature(T::class)
|
||||||
|
|
||||||
|
//public class OptimizationResult<T>(
|
||||||
|
// public val point: Map<Symbol, T>,
|
||||||
|
// public val value: T,
|
||||||
|
// public val features: Set<OptimizationFeature> = emptySet(),
|
||||||
|
//) {
|
||||||
|
// override fun toString(): String {
|
||||||
|
// return "OptimizationResult(point=$point, value=$value)"
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//public operator fun <T> OptimizationResult<T>.plus(
|
||||||
|
// feature: OptimizationFeature,
|
||||||
|
//): OptimizationResult<T> = OptimizationResult(point, value, features + feature)
|
||||||
|
//public fun interface OptimizationProblemFactory<T : Any, out P : OptimizationProblem<T>> {
|
||||||
|
// public fun build(symbols: List<Symbol>): P
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//public operator fun <T : Any, P : OptimizationProblem<T>> OptimizationProblemFactory<T, P>.invoke(
|
||||||
|
// symbols: List<Symbol>,
|
||||||
|
// block: P.() -> Unit,
|
||||||
|
//): P = build(symbols).apply(block)
|
||||||
|
|
||||||
|
public interface Optimizer<P : OptimizationProblem> {
|
||||||
|
public suspend fun process(problem: P): P
|
||||||
|
}
|
||||||
|
|
@ -16,7 +16,7 @@ import space.kscience.kmath.operations.ExtendedField
|
|||||||
import space.kscience.kmath.operations.Field
|
import space.kscience.kmath.operations.Field
|
||||||
|
|
||||||
@UnstableKMathAPI
|
@UnstableKMathAPI
|
||||||
public interface XYFit<T : Any> : Optimization<T> {
|
public interface XYFit<T> : OptimizationProblem<T> {
|
||||||
|
|
||||||
public val algebra: Field<T>
|
public val algebra: Field<T>
|
||||||
|
|
||||||
|
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import ru.inr.mass.maths.MultiFunction
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
internal class AnalyticalGradientCalculator(fcn: MultiFunction?, state: MnUserTransformation, checkGradient: Boolean) :
|
||||||
|
GradientCalculator {
|
||||||
|
private val function: MultiFunction?
|
||||||
|
private val theCheckGradient: Boolean
|
||||||
|
private val theTransformation: MnUserTransformation
|
||||||
|
fun checkGradient(): Boolean {
|
||||||
|
return theCheckGradient
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
fun gradient(par: MinimumParameters): FunctionGradient {
|
||||||
|
// double[] grad = theGradCalc.gradientValue(theTransformation.andThen(par.vec()).data());
|
||||||
|
val point: DoubleArray = theTransformation.transform(par.vec()).toArray()
|
||||||
|
require(!(function.getDimension() !== theTransformation.parameters().size())) { "Invalid parameter size" }
|
||||||
|
val v: RealVector = ArrayRealVector(par.vec().getDimension())
|
||||||
|
for (i in 0 until par.vec().getDimension()) {
|
||||||
|
val ext: Int = theTransformation.extOfInt(i)
|
||||||
|
if (theTransformation.parameter(ext).hasLimits()) {
|
||||||
|
val dd: Double = theTransformation.dInt2Ext(i, par.vec().getEntry(i))
|
||||||
|
v.setEntry(i, dd * function.derivValue(ext, point))
|
||||||
|
} else {
|
||||||
|
v.setEntry(i, function.derivValue(ext, point))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FunctionGradient(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
fun gradient(par: MinimumParameters, grad: FunctionGradient?): FunctionGradient {
|
||||||
|
return gradient(par)
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
function = fcn
|
||||||
|
theTransformation = state
|
||||||
|
theCheckGradient = checkGradient
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
internal class CombinedMinimizer : ModularFunctionMinimizer() {
|
||||||
|
private val theMinBuilder: CombinedMinimumBuilder = CombinedMinimumBuilder()
|
||||||
|
private val theMinSeedGen: MnSeedGenerator = MnSeedGenerator()
|
||||||
|
override fun builder(): MinimumBuilder {
|
||||||
|
return theMinBuilder
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun seedGenerator(): MinimumSeedGenerator {
|
||||||
|
return theMinSeedGen
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import space.kscience.kmath.optimization.minuit.MINUITPlugin
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
internal class CombinedMinimumBuilder : MinimumBuilder {
|
||||||
|
private val theSimplexMinimizer: SimplexMinimizer = SimplexMinimizer()
|
||||||
|
private val theVMMinimizer: VariableMetricMinimizer = VariableMetricMinimizer()
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
override fun minimum(
|
||||||
|
fcn: MnFcn?,
|
||||||
|
gc: GradientCalculator?,
|
||||||
|
seed: MinimumSeed?,
|
||||||
|
strategy: MnStrategy?,
|
||||||
|
maxfcn: Int,
|
||||||
|
toler: Double
|
||||||
|
): FunctionMinimum {
|
||||||
|
val min: FunctionMinimum = theVMMinimizer.minimize(fcn!!, gc, seed, strategy, maxfcn, toler)
|
||||||
|
if (!min.isValid()) {
|
||||||
|
MINUITPlugin.logStatic("CombinedMinimumBuilder: migrad method fails, will try with simplex method first.")
|
||||||
|
val str = MnStrategy(2)
|
||||||
|
val min1: FunctionMinimum = theSimplexMinimizer.minimize(fcn, gc, seed, str, maxfcn, toler)
|
||||||
|
if (!min1.isValid()) {
|
||||||
|
MINUITPlugin.logStatic("CombinedMinimumBuilder: both migrad and simplex method fail.")
|
||||||
|
return min1
|
||||||
|
}
|
||||||
|
val seed1: MinimumSeed = theVMMinimizer.seedGenerator().generate(fcn, gc, min1.userState(), str)
|
||||||
|
val min2: FunctionMinimum = theVMMinimizer.minimize(fcn, gc, seed1, str, maxfcn, toler)
|
||||||
|
if (!min2.isValid()) {
|
||||||
|
MINUITPlugin.logStatic("CombinedMinimumBuilder: both migrad and method fails also at 2nd attempt.")
|
||||||
|
MINUITPlugin.logStatic("CombinedMinimumBuilder: return simplex minimum.")
|
||||||
|
return min1
|
||||||
|
}
|
||||||
|
return min2
|
||||||
|
}
|
||||||
|
return min
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,150 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* ContoursError class.
|
||||||
|
*
|
||||||
|
* @author Darksnake
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
class ContoursError internal constructor(
|
||||||
|
private val theParX: Int,
|
||||||
|
private val theParY: Int,
|
||||||
|
points: List<Range>,
|
||||||
|
xmnos: MinosError,
|
||||||
|
ymnos: MinosError,
|
||||||
|
nfcn: Int
|
||||||
|
) {
|
||||||
|
private val theNFcn: Int
|
||||||
|
private val thePoints: List<Range> = points
|
||||||
|
private val theXMinos: MinosError
|
||||||
|
private val theYMinos: MinosError
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* nfcn.
|
||||||
|
*
|
||||||
|
* @return a int.
|
||||||
|
*/
|
||||||
|
fun nfcn(): Int {
|
||||||
|
return theNFcn
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* points.
|
||||||
|
*
|
||||||
|
* @return a [List] object.
|
||||||
|
*/
|
||||||
|
fun points(): List<Range> {
|
||||||
|
return thePoints
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
override fun toString(): String {
|
||||||
|
return MnPrint.toString(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* xMinosError.
|
||||||
|
*
|
||||||
|
* @return a [hep.dataforge.MINUIT.MinosError] object.
|
||||||
|
*/
|
||||||
|
fun xMinosError(): MinosError {
|
||||||
|
return theXMinos
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* xRange.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
fun xRange(): Range {
|
||||||
|
return theXMinos.range()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* xmin.
|
||||||
|
*
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
fun xmin(): Double {
|
||||||
|
return theXMinos.min()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* xpar.
|
||||||
|
*
|
||||||
|
* @return a int.
|
||||||
|
*/
|
||||||
|
fun xpar(): Int {
|
||||||
|
return theParX
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* yMinosError.
|
||||||
|
*
|
||||||
|
* @return a [hep.dataforge.MINUIT.MinosError] object.
|
||||||
|
*/
|
||||||
|
fun yMinosError(): MinosError {
|
||||||
|
return theYMinos
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* yRange.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
fun yRange(): Range {
|
||||||
|
return theYMinos.range()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* ymin.
|
||||||
|
*
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
fun ymin(): Double {
|
||||||
|
return theYMinos.min()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* ypar.
|
||||||
|
*
|
||||||
|
* @return a int.
|
||||||
|
*/
|
||||||
|
fun ypar(): Int {
|
||||||
|
return theParY
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
theXMinos = xmnos
|
||||||
|
theYMinos = ymnos
|
||||||
|
theNFcn = nfcn
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import org.apache.commons.math3.linear.RealVector
|
||||||
|
import ru.inr.mass.minuit.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
internal class DavidonErrorUpdator : MinimumErrorUpdator {
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
fun update(s0: MinimumState, p1: MinimumParameters, g1: FunctionGradient): MinimumError {
|
||||||
|
val V0: MnAlgebraicSymMatrix = s0.error().invHessian()
|
||||||
|
val dx: RealVector = MnUtils.sub(p1.vec(), s0.vec())
|
||||||
|
val dg: RealVector = MnUtils.sub(g1.getGradient(), s0.gradient().getGradient())
|
||||||
|
val delgam: Double = MnUtils.innerProduct(dx, dg)
|
||||||
|
val gvg: Double = MnUtils.similarity(dg, V0)
|
||||||
|
val vg: RealVector = MnUtils.mul(V0, dg)
|
||||||
|
var Vupd: MnAlgebraicSymMatrix =
|
||||||
|
MnUtils.sub(MnUtils.div(MnUtils.outerProduct(dx), delgam), MnUtils.div(MnUtils.outerProduct(vg), gvg))
|
||||||
|
if (delgam > gvg) {
|
||||||
|
Vupd = MnUtils.add(Vupd,
|
||||||
|
MnUtils.mul(MnUtils.outerProduct(MnUtils.sub(MnUtils.div(dx, delgam), MnUtils.div(vg, gvg))), gvg))
|
||||||
|
}
|
||||||
|
val sum_upd: Double = MnUtils.absoluteSumOfElements(Vupd)
|
||||||
|
Vupd = MnUtils.add(Vupd, V0)
|
||||||
|
val dcov: Double = 0.5 * (s0.error().dcovar() + sum_upd / MnUtils.absoluteSumOfElements(Vupd))
|
||||||
|
return MinimumError(Vupd, dcov)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import org.apache.commons.math3.linear.ArrayRealVector
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
class FunctionGradient {
|
||||||
|
private var theAnalytical = false
|
||||||
|
private var theG2ndDerivative: RealVector
|
||||||
|
private var theGStepSize: RealVector
|
||||||
|
private var theGradient: RealVector
|
||||||
|
private var theValid = false
|
||||||
|
|
||||||
|
constructor(n: Int) {
|
||||||
|
theGradient = ArrayRealVector(n)
|
||||||
|
theG2ndDerivative = ArrayRealVector(n)
|
||||||
|
theGStepSize = ArrayRealVector(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(grd: RealVector) {
|
||||||
|
theGradient = grd
|
||||||
|
theG2ndDerivative = ArrayRealVector(grd.getDimension())
|
||||||
|
theGStepSize = ArrayRealVector(grd.getDimension())
|
||||||
|
theValid = true
|
||||||
|
theAnalytical = true
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(grd: RealVector, g2: RealVector, gstep: RealVector) {
|
||||||
|
theGradient = grd
|
||||||
|
theG2ndDerivative = g2
|
||||||
|
theGStepSize = gstep
|
||||||
|
theValid = true
|
||||||
|
theAnalytical = false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getGradient(): RealVector {
|
||||||
|
return theGradient
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getGradientDerivative(): RealVector {
|
||||||
|
return theG2ndDerivative
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getStep(): RealVector {
|
||||||
|
return theGStepSize
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isAnalytical(): Boolean {
|
||||||
|
return theAnalytical
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isValid(): Boolean {
|
||||||
|
return theValid
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,259 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import ru.inr.mass.minuit.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of the minimization.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* The FunctionMinimum is the output of the minimizers and contains the
|
||||||
|
* minimization result. The methods
|
||||||
|
*
|
||||||
|
* * userState(),
|
||||||
|
* * userParameters() and
|
||||||
|
* * userCovariance()
|
||||||
|
*
|
||||||
|
* are provided. These can be used as new input to a new minimization after some
|
||||||
|
* manipulation. The parameters and/or the FunctionMinimum can be printed using
|
||||||
|
* the toString() method or the MnPrint class.
|
||||||
|
*
|
||||||
|
* @author Darksnake
|
||||||
|
*/
|
||||||
|
class FunctionMinimum {
|
||||||
|
private var theAboveMaxEdm = false
|
||||||
|
private var theErrorDef: Double
|
||||||
|
private var theReachedCallLimit = false
|
||||||
|
private var theSeed: MinimumSeed
|
||||||
|
private var theStates: MutableList<MinimumState>
|
||||||
|
private var theUserState: MnUserParameterState
|
||||||
|
|
||||||
|
internal constructor(seed: MinimumSeed, up: Double) {
|
||||||
|
theSeed = seed
|
||||||
|
theStates = ArrayList()
|
||||||
|
theStates.add(MinimumState(seed.parameters(),
|
||||||
|
seed.error(),
|
||||||
|
seed.gradient(),
|
||||||
|
seed.parameters().fval(),
|
||||||
|
seed.nfcn()))
|
||||||
|
theErrorDef = up
|
||||||
|
theUserState = MnUserParameterState()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal constructor(seed: MinimumSeed, states: MutableList<MinimumState>, up: Double) {
|
||||||
|
theSeed = seed
|
||||||
|
theStates = states
|
||||||
|
theErrorDef = up
|
||||||
|
theUserState = MnUserParameterState()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal constructor(seed: MinimumSeed, states: MutableList<MinimumState>, up: Double, x: MnReachedCallLimit?) {
|
||||||
|
theSeed = seed
|
||||||
|
theStates = states
|
||||||
|
theErrorDef = up
|
||||||
|
theReachedCallLimit = true
|
||||||
|
theUserState = MnUserParameterState()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal constructor(seed: MinimumSeed, states: MutableList<MinimumState>, up: Double, x: MnAboveMaxEdm?) {
|
||||||
|
theSeed = seed
|
||||||
|
theStates = states
|
||||||
|
theErrorDef = up
|
||||||
|
theAboveMaxEdm = true
|
||||||
|
theReachedCallLimit = false
|
||||||
|
theUserState = MnUserParameterState()
|
||||||
|
}
|
||||||
|
|
||||||
|
// why not
|
||||||
|
fun add(state: MinimumState) {
|
||||||
|
theStates.add(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the expected vertical distance to the minimum (EDM)
|
||||||
|
*
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
fun edm(): Double {
|
||||||
|
return lastState().edm()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun error(): MinimumError {
|
||||||
|
return lastState().error()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* errorDef.
|
||||||
|
*
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
fun errorDef(): Double {
|
||||||
|
return theErrorDef
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the function value at the minimum.
|
||||||
|
*
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
fun fval(): Double {
|
||||||
|
return lastState().fval()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun grad(): FunctionGradient {
|
||||||
|
return lastState().gradient()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasAccurateCovar(): Boolean {
|
||||||
|
return state().error().isAccurate()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasCovariance(): Boolean {
|
||||||
|
return state().error().isAvailable()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasMadePosDefCovar(): Boolean {
|
||||||
|
return state().error().isMadePosDef()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasPosDefCovar(): Boolean {
|
||||||
|
return state().error().isPosDef()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasReachedCallLimit(): Boolean {
|
||||||
|
return theReachedCallLimit
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasValidCovariance(): Boolean {
|
||||||
|
return state().error().isValid()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasValidParameters(): Boolean {
|
||||||
|
return state().parameters().isValid()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hesseFailed(): Boolean {
|
||||||
|
return state().error().hesseFailed()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isAboveMaxEdm(): Boolean {
|
||||||
|
return theAboveMaxEdm
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In general, if this returns <CODE>true</CODE>, the minimizer did find a
|
||||||
|
* minimum without running into troubles. However, in some cases a minimum
|
||||||
|
* cannot be found, then the return value will be <CODE>false</CODE>.
|
||||||
|
* Reasons for the minimization to fail are
|
||||||
|
*
|
||||||
|
* * the number of allowed function calls has been exhausted
|
||||||
|
* * the minimizer could not improve the values of the parameters (and
|
||||||
|
* knowing that it has not converged yet)
|
||||||
|
* * a problem with the calculation of the covariance matrix
|
||||||
|
*
|
||||||
|
* Additional methods for the analysis of the state at the minimum are
|
||||||
|
* provided.
|
||||||
|
*
|
||||||
|
* @return a boolean.
|
||||||
|
*/
|
||||||
|
fun isValid(): Boolean {
|
||||||
|
return state().isValid() && !isAboveMaxEdm() && !hasReachedCallLimit()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun lastState(): MinimumState {
|
||||||
|
return theStates[theStates.size - 1]
|
||||||
|
}
|
||||||
|
// forward interface of last state
|
||||||
|
/**
|
||||||
|
* returns the total number of function calls during the minimization.
|
||||||
|
*
|
||||||
|
* @return a int.
|
||||||
|
*/
|
||||||
|
fun nfcn(): Int {
|
||||||
|
return lastState().nfcn()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun parameters(): MinimumParameters {
|
||||||
|
return lastState().parameters()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun seed(): MinimumSeed {
|
||||||
|
return theSeed
|
||||||
|
}
|
||||||
|
|
||||||
|
fun state(): MinimumState {
|
||||||
|
return lastState()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun states(): List<MinimumState> {
|
||||||
|
return theStates
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
override fun toString(): String {
|
||||||
|
return MnPrint.toString(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* userCovariance.
|
||||||
|
*
|
||||||
|
* @return a [hep.dataforge.MINUIT.MnUserCovariance] object.
|
||||||
|
*/
|
||||||
|
fun userCovariance(): MnUserCovariance {
|
||||||
|
if (!theUserState.isValid()) {
|
||||||
|
theUserState = MnUserParameterState(state(), errorDef(), seed().trafo())
|
||||||
|
}
|
||||||
|
return theUserState.covariance()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* userParameters.
|
||||||
|
*
|
||||||
|
* @return a [hep.dataforge.MINUIT.MnUserParameters] object.
|
||||||
|
*/
|
||||||
|
fun userParameters(): MnUserParameters {
|
||||||
|
if (!theUserState.isValid()) {
|
||||||
|
theUserState = MnUserParameterState(state(), errorDef(), seed().trafo())
|
||||||
|
}
|
||||||
|
return theUserState.parameters()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* user representation of state at minimum
|
||||||
|
*
|
||||||
|
* @return a [hep.dataforge.MINUIT.MnUserParameterState] object.
|
||||||
|
*/
|
||||||
|
fun userState(): MnUserParameterState {
|
||||||
|
if (!theUserState.isValid()) {
|
||||||
|
theUserState = MnUserParameterState(state(), errorDef(), seed().trafo())
|
||||||
|
}
|
||||||
|
return theUserState
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class MnAboveMaxEdm
|
||||||
|
internal class MnReachedCallLimit
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
interface GradientCalculator {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* gradient.
|
||||||
|
*
|
||||||
|
* @param par a [hep.dataforge.MINUIT.MinimumParameters] object.
|
||||||
|
* @return a [hep.dataforge.MINUIT.FunctionGradient] object.
|
||||||
|
*/
|
||||||
|
fun gradient(par: MinimumParameters?): FunctionGradient
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* gradient.
|
||||||
|
*
|
||||||
|
* @param par a [hep.dataforge.MINUIT.MinimumParameters] object.
|
||||||
|
* @param grad a [hep.dataforge.MINUIT.FunctionGradient] object.
|
||||||
|
* @return a [hep.dataforge.MINUIT.FunctionGradient] object.
|
||||||
|
*/
|
||||||
|
fun gradient(par: MinimumParameters?, grad: FunctionGradient?): FunctionGradient
|
||||||
|
}
|
@ -0,0 +1,137 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import org.apache.commons.math3.linear.ArrayRealVector
|
||||||
|
import ru.inr.mass.minuit.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
internal class HessianGradientCalculator(fcn: MnFcn, par: MnUserTransformation, stra: MnStrategy) : GradientCalculator {
|
||||||
|
private val theFcn: MnFcn = fcn
|
||||||
|
private val theStrategy: MnStrategy
|
||||||
|
private val theTransformation: MnUserTransformation
|
||||||
|
fun deltaGradient(par: MinimumParameters, gradient: FunctionGradient): Pair<FunctionGradient, RealVector> {
|
||||||
|
require(par.isValid()) { "parameters are invalid" }
|
||||||
|
val x: RealVector = par.vec().copy()
|
||||||
|
val grd: RealVector = gradient.getGradient().copy()
|
||||||
|
val g2: RealVector = gradient.getGradientDerivative()
|
||||||
|
val gstep: RealVector = gradient.getStep()
|
||||||
|
val fcnmin: Double = par.fval()
|
||||||
|
// std::cout<<"fval: "<<fcnmin<<std::endl;
|
||||||
|
val dfmin: Double = 4.0 * precision().eps2() * (abs(fcnmin) + theFcn.errorDef())
|
||||||
|
val n: Int = x.getDimension()
|
||||||
|
val dgrd: RealVector = ArrayRealVector(n)
|
||||||
|
|
||||||
|
// initial starting values
|
||||||
|
for (i in 0 until n) {
|
||||||
|
val xtf: Double = x.getEntry(i)
|
||||||
|
val dmin: Double = 4.0 * precision().eps2() * (xtf + precision().eps2())
|
||||||
|
val epspri: Double = precision().eps2() + abs(grd.getEntry(i) * precision().eps2())
|
||||||
|
val optstp: Double = sqrt(dfmin / (abs(g2.getEntry(i)) + epspri))
|
||||||
|
var d: Double = 0.2 * abs(gstep.getEntry(i))
|
||||||
|
if (d > optstp) {
|
||||||
|
d = optstp
|
||||||
|
}
|
||||||
|
if (d < dmin) {
|
||||||
|
d = dmin
|
||||||
|
}
|
||||||
|
var chgold = 10000.0
|
||||||
|
var dgmin = 0.0
|
||||||
|
var grdold = 0.0
|
||||||
|
var grdnew = 0.0
|
||||||
|
for (j in 0 until ncycle()) {
|
||||||
|
x.setEntry(i, xtf + d)
|
||||||
|
val fs1: Double = theFcn.value(x)
|
||||||
|
x.setEntry(i, xtf - d)
|
||||||
|
val fs2: Double = theFcn.value(x)
|
||||||
|
x.setEntry(i, xtf)
|
||||||
|
// double sag = 0.5*(fs1+fs2-2.*fcnmin);
|
||||||
|
grdold = grd.getEntry(i)
|
||||||
|
grdnew = (fs1 - fs2) / (2.0 * d)
|
||||||
|
dgmin = precision().eps() * (abs(fs1) + abs(fs2)) / d
|
||||||
|
if (abs(grdnew) < precision().eps()) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
val change: Double = abs((grdold - grdnew) / grdnew)
|
||||||
|
if (change > chgold && j > 1) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
chgold = change
|
||||||
|
grd.setEntry(i, grdnew)
|
||||||
|
if (change < 0.05) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (abs(grdold - grdnew) < dgmin) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (d < dmin) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
d *= 0.2
|
||||||
|
}
|
||||||
|
dgrd.setEntry(i, max(dgmin, abs(grdold - grdnew)))
|
||||||
|
}
|
||||||
|
return Pair(FunctionGradient(grd, g2, gstep), dgrd)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fcn(): MnFcn {
|
||||||
|
return theFcn
|
||||||
|
}
|
||||||
|
|
||||||
|
fun gradTolerance(): Double {
|
||||||
|
return strategy().gradientTolerance()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
fun gradient(par: MinimumParameters): FunctionGradient {
|
||||||
|
val gc = InitialGradientCalculator(theFcn, theTransformation, theStrategy)
|
||||||
|
val gra: FunctionGradient = gc.gradient(par)
|
||||||
|
return gradient(par, gra)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
fun gradient(par: MinimumParameters, gradient: FunctionGradient): FunctionGradient {
|
||||||
|
return deltaGradient(par, gradient).getFirst()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ncycle(): Int {
|
||||||
|
return strategy().hessianGradientNCycles()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun precision(): MnMachinePrecision {
|
||||||
|
return theTransformation.precision()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stepTolerance(): Double {
|
||||||
|
return strategy().gradientStepTolerance()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun strategy(): MnStrategy {
|
||||||
|
return theStrategy
|
||||||
|
}
|
||||||
|
|
||||||
|
fun trafo(): MnUserTransformation {
|
||||||
|
return theTransformation
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
theTransformation = par
|
||||||
|
theStrategy = stra
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import org.apache.commons.math3.linear.ArrayRealVector
|
||||||
|
import ru.inr.mass.minuit.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculating derivatives via finite differences
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
internal class InitialGradientCalculator(fcn: MnFcn, par: MnUserTransformation, stra: MnStrategy) {
|
||||||
|
private val theFcn: MnFcn = fcn
|
||||||
|
private val theStrategy: MnStrategy
|
||||||
|
private val theTransformation: MnUserTransformation
|
||||||
|
fun fcn(): MnFcn {
|
||||||
|
return theFcn
|
||||||
|
}
|
||||||
|
|
||||||
|
fun gradTolerance(): Double {
|
||||||
|
return strategy().gradientTolerance()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun gradient(par: MinimumParameters): FunctionGradient {
|
||||||
|
require(par.isValid()) { "Parameters are invalid" }
|
||||||
|
val n: Int = trafo().variableParameters()
|
||||||
|
require(n == par.vec().getDimension()) { "Parameters have invalid size" }
|
||||||
|
val gr: RealVector = ArrayRealVector(n)
|
||||||
|
val gr2: RealVector = ArrayRealVector(n)
|
||||||
|
val gst: RealVector = ArrayRealVector(n)
|
||||||
|
|
||||||
|
// initial starting values
|
||||||
|
for (i in 0 until n) {
|
||||||
|
val exOfIn: Int = trafo().extOfInt(i)
|
||||||
|
val `var`: Double = par.vec().getEntry(i) //parameter value
|
||||||
|
val werr: Double = trafo().parameter(exOfIn).error() //parameter error
|
||||||
|
val sav: Double = trafo().int2ext(i, `var`) //value after transformation
|
||||||
|
var sav2 = sav + werr //value after transfomation + error
|
||||||
|
if (trafo().parameter(exOfIn).hasLimits()) {
|
||||||
|
if (trafo().parameter(exOfIn).hasUpperLimit()
|
||||||
|
&& sav2 > trafo().parameter(exOfIn).upperLimit()
|
||||||
|
) {
|
||||||
|
sav2 = trafo().parameter(exOfIn).upperLimit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var var2: Double = trafo().ext2int(exOfIn, sav2)
|
||||||
|
val vplu = var2 - `var`
|
||||||
|
sav2 = sav - werr
|
||||||
|
if (trafo().parameter(exOfIn).hasLimits()) {
|
||||||
|
if (trafo().parameter(exOfIn).hasLowerLimit()
|
||||||
|
&& sav2 < trafo().parameter(exOfIn).lowerLimit()
|
||||||
|
) {
|
||||||
|
sav2 = trafo().parameter(exOfIn).lowerLimit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var2 = trafo().ext2int(exOfIn, sav2)
|
||||||
|
val vmin = var2 - `var`
|
||||||
|
val dirin: Double = 0.5 * (abs(vplu) + abs(vmin))
|
||||||
|
val g2: Double = 2.0 * theFcn.errorDef() / (dirin * dirin)
|
||||||
|
val gsmin: Double = 8.0 * precision().eps2() * (abs(`var`) + precision().eps2())
|
||||||
|
var gstep: Double = max(gsmin, 0.1 * dirin)
|
||||||
|
val grd = g2 * dirin
|
||||||
|
if (trafo().parameter(exOfIn).hasLimits()) {
|
||||||
|
if (gstep > 0.5) {
|
||||||
|
gstep = 0.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gr.setEntry(i, grd)
|
||||||
|
gr2.setEntry(i, g2)
|
||||||
|
gst.setEntry(i, gstep)
|
||||||
|
}
|
||||||
|
return FunctionGradient(gr, gr2, gst)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun gradient(par: MinimumParameters, gra: FunctionGradient?): FunctionGradient {
|
||||||
|
return gradient(par)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ncycle(): Int {
|
||||||
|
return strategy().gradientNCycles()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun precision(): MnMachinePrecision {
|
||||||
|
return theTransformation.precision()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stepTolerance(): Double {
|
||||||
|
return strategy().gradientStepTolerance()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun strategy(): MnStrategy {
|
||||||
|
return theStrategy
|
||||||
|
}
|
||||||
|
|
||||||
|
fun trafo(): MnUserTransformation {
|
||||||
|
return theTransformation
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
theTransformation = par
|
||||||
|
theStrategy = stra
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package space.kscience.kmath.optimization.minuit
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Контейнер для несимметричных оценок и доверительных интервалов
|
||||||
|
*
|
||||||
|
* @author Darksnake
|
||||||
|
* @version $Id: $Id
|
||||||
|
*/
|
||||||
|
class MINOSResult
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Constructor for MINOSResult.
|
||||||
|
*
|
||||||
|
* @param list an array of [String] objects.
|
||||||
|
*/(private val names: Array<String>, private val errl: DoubleArray?, private val errp: DoubleArray?) :
|
||||||
|
IntervalEstimate {
|
||||||
|
fun getNames(): NameList {
|
||||||
|
return NameList(names)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getInterval(parName: String?): Pair<Value, Value> {
|
||||||
|
val index: Int = getNames().getNumberByName(parName)
|
||||||
|
return Pair(ValueFactory.of(errl!![index]), ValueFactory.of(errp!![index]))
|
||||||
|
}
|
||||||
|
|
||||||
|
val cL: Double
|
||||||
|
get() = 0.68
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
fun print(out: PrintWriter) {
|
||||||
|
if (errl != null || errp != null) {
|
||||||
|
out.println()
|
||||||
|
out.println("Assymetrical errors:")
|
||||||
|
out.println()
|
||||||
|
out.println("Name\tLower\tUpper")
|
||||||
|
for (i in 0 until getNames().size()) {
|
||||||
|
out.print(getNames().get(i))
|
||||||
|
out.print("\t")
|
||||||
|
if (errl != null) {
|
||||||
|
out.print(errl[i])
|
||||||
|
} else {
|
||||||
|
out.print("---")
|
||||||
|
}
|
||||||
|
out.print("\t")
|
||||||
|
if (errp != null) {
|
||||||
|
out.print(errp[i])
|
||||||
|
} else {
|
||||||
|
out.print("---")
|
||||||
|
}
|
||||||
|
out.println()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,205 @@
|
|||||||
|
/*
|
||||||
|
* 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.minuit
|
||||||
|
|
||||||
|
import ru.inr.mass.minuit.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* MINUITFitter class.
|
||||||
|
*
|
||||||
|
* @author Darksnake
|
||||||
|
* @version $Id: $Id
|
||||||
|
*/
|
||||||
|
class MINUITFitter : Fitter {
|
||||||
|
fun run(state: FitState, parentLog: History?, meta: Meta): FitResult {
|
||||||
|
val log = Chronicle("MINUIT", parentLog)
|
||||||
|
val action: String = meta.getString("action", TASK_RUN)
|
||||||
|
log.report("MINUIT fit engine started action '{}'", action)
|
||||||
|
return when (action) {
|
||||||
|
TASK_COVARIANCE -> runHesse(state, log, meta)
|
||||||
|
TASK_SINGLE, TASK_RUN -> runFit(state, log, meta)
|
||||||
|
else -> throw IllegalArgumentException("Unknown task")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
fun getName(): String {
|
||||||
|
return MINUIT_ENGINE_NAME
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* runHesse.
|
||||||
|
*
|
||||||
|
* @param state a [hep.dataforge.stat.fit.FitState] object.
|
||||||
|
* @param log
|
||||||
|
* @return a [FitResult] object.
|
||||||
|
*/
|
||||||
|
fun runHesse(state: FitState, log: History, meta: Meta?): FitResult {
|
||||||
|
val strategy: Int
|
||||||
|
strategy = Global.INSTANCE.getInt("MINUIT_STRATEGY", 2)
|
||||||
|
log.report("Generating errors using MnHesse 2-nd order gradient calculator.")
|
||||||
|
val fcn: MultiFunction
|
||||||
|
val fitPars: Array<String> = Fitter.Companion.getFitPars(state, meta)
|
||||||
|
val pars: ParamSet = state.getParameters()
|
||||||
|
fcn = MINUITUtils.getFcn(state, pars, fitPars)
|
||||||
|
val hesse = MnHesse(strategy)
|
||||||
|
val mnState: MnUserParameterState = hesse.calculate(fcn, MINUITUtils.getFitParameters(pars, fitPars))
|
||||||
|
val allPars: ParamSet = pars.copy()
|
||||||
|
for (fitPar in fitPars) {
|
||||||
|
allPars.setParValue(fitPar, mnState.value(fitPar))
|
||||||
|
allPars.setParError(fitPar, mnState.error(fitPar))
|
||||||
|
}
|
||||||
|
val newState: FitState.Builder = state.edit()
|
||||||
|
newState.setPars(allPars)
|
||||||
|
if (mnState.hasCovariance()) {
|
||||||
|
val mnCov: MnUserCovariance = mnState.covariance()
|
||||||
|
var j: Int
|
||||||
|
val cov = Array(mnState.variableParameters()) { DoubleArray(mnState.variableParameters()) }
|
||||||
|
for (i in 0 until mnState.variableParameters()) {
|
||||||
|
j = 0
|
||||||
|
while (j < mnState.variableParameters()) {
|
||||||
|
cov[i][j] = mnCov.get(i, j)
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newState.setCovariance(NamedMatrix(fitPars, cov), true)
|
||||||
|
}
|
||||||
|
return FitResult.build(newState.build(), fitPars)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun runFit(state: FitState, log: History, meta: Meta): FitResult {
|
||||||
|
val minuit: MnApplication
|
||||||
|
log.report("Starting fit using Minuit.")
|
||||||
|
val strategy: Int
|
||||||
|
strategy = Global.INSTANCE.getInt("MINUIT_STRATEGY", 2)
|
||||||
|
var force: Boolean
|
||||||
|
force = Global.INSTANCE.getBoolean("FORCE_DERIVS", false)
|
||||||
|
val fitPars: Array<String> = Fitter.Companion.getFitPars(state, meta)
|
||||||
|
for (fitPar in fitPars) {
|
||||||
|
if (!state.modelProvidesDerivs(fitPar)) {
|
||||||
|
force = true
|
||||||
|
log.reportError("Model does not provide derivatives for parameter '{}'", fitPar)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (force) {
|
||||||
|
log.report("Using MINUIT gradient calculator.")
|
||||||
|
}
|
||||||
|
val fcn: MultiFunction
|
||||||
|
val pars: ParamSet = state.getParameters().copy()
|
||||||
|
fcn = MINUITUtils.getFcn(state, pars, fitPars)
|
||||||
|
val method: String = meta.getString("method", MINUIT_MIGRAD)
|
||||||
|
when (method) {
|
||||||
|
MINUIT_MINOS, MINUIT_MINIMIZE -> minuit =
|
||||||
|
MnMinimize(fcn, MINUITUtils.getFitParameters(pars, fitPars), strategy)
|
||||||
|
MINUIT_SIMPLEX -> minuit = MnSimplex(fcn, MINUITUtils.getFitParameters(pars, fitPars), strategy)
|
||||||
|
else -> minuit = MnMigrad(fcn, MINUITUtils.getFitParameters(pars, fitPars), strategy)
|
||||||
|
}
|
||||||
|
if (force) {
|
||||||
|
minuit.setUseAnalyticalDerivatives(false)
|
||||||
|
log.report("Forced to use MINUIT internal derivative calculator!")
|
||||||
|
}
|
||||||
|
|
||||||
|
// minuit.setUseAnalyticalDerivatives(true);
|
||||||
|
val minimum: FunctionMinimum
|
||||||
|
val maxSteps: Int = meta.getInt("iterations", -1)
|
||||||
|
val tolerance: Double = meta.getDouble("tolerance", -1)
|
||||||
|
minimum = if (maxSteps > 0) {
|
||||||
|
if (tolerance > 0) {
|
||||||
|
minuit.minimize(maxSteps, tolerance)
|
||||||
|
} else {
|
||||||
|
minuit.minimize(maxSteps)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
minuit.minimize()
|
||||||
|
}
|
||||||
|
if (!minimum.isValid()) {
|
||||||
|
log.report("Minimization failed!")
|
||||||
|
}
|
||||||
|
log.report("MINUIT run completed in {} function calls.", minimum.nfcn())
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Генерация результата
|
||||||
|
*/
|
||||||
|
val allPars: ParamSet = pars.copy()
|
||||||
|
for (fitPar in fitPars) {
|
||||||
|
allPars.setParValue(fitPar, minimum.userParameters().value(fitPar))
|
||||||
|
allPars.setParError(fitPar, minimum.userParameters().error(fitPar))
|
||||||
|
}
|
||||||
|
val newState: FitState.Builder = state.edit()
|
||||||
|
newState.setPars(allPars)
|
||||||
|
var valid: Boolean = minimum.isValid()
|
||||||
|
if (minimum.userCovariance().nrow() > 0) {
|
||||||
|
var j: Int
|
||||||
|
val cov = Array(minuit.variableParameters()) { DoubleArray(minuit.variableParameters()) }
|
||||||
|
if (cov[0].length == 1) {
|
||||||
|
cov[0][0] = minimum.userParameters().error(0) * minimum.userParameters().error(0)
|
||||||
|
} else {
|
||||||
|
for (i in 0 until minuit.variableParameters()) {
|
||||||
|
j = 0
|
||||||
|
while (j < minuit.variableParameters()) {
|
||||||
|
cov[i][j] = minimum.userCovariance().get(i, j)
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newState.setCovariance(NamedMatrix(fitPars, cov), true)
|
||||||
|
}
|
||||||
|
if (method == MINUIT_MINOS) {
|
||||||
|
log.report("Starting MINOS procedure for precise error estimation.")
|
||||||
|
val minos = MnMinos(fcn, minimum, strategy)
|
||||||
|
var mnError: MinosError
|
||||||
|
val errl = DoubleArray(fitPars.size)
|
||||||
|
val errp = DoubleArray(fitPars.size)
|
||||||
|
for (i in fitPars.indices) {
|
||||||
|
mnError = minos.minos(i)
|
||||||
|
if (mnError.isValid()) {
|
||||||
|
errl[i] = mnError.lower()
|
||||||
|
errp[i] = mnError.upper()
|
||||||
|
} else {
|
||||||
|
valid = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val minosErrors = MINOSResult(fitPars, errl, errp)
|
||||||
|
newState.setInterval(minosErrors)
|
||||||
|
}
|
||||||
|
return FitResult.build(newState.build(), valid, fitPars)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Constant `MINUIT_MIGRAD="MIGRAD"`
|
||||||
|
*/
|
||||||
|
const val MINUIT_MIGRAD = "MIGRAD"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant `MINUIT_MINIMIZE="MINIMIZE"`
|
||||||
|
*/
|
||||||
|
const val MINUIT_MINIMIZE = "MINIMIZE"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant `MINUIT_SIMPLEX="SIMPLEX"`
|
||||||
|
*/
|
||||||
|
const val MINUIT_SIMPLEX = "SIMPLEX"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant `MINUIT_MINOS="MINOS"`
|
||||||
|
*/
|
||||||
|
const val MINUIT_MINOS = "MINOS" //MINOS errors
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant `MINUIT_HESSE="HESSE"`
|
||||||
|
*/
|
||||||
|
const val MINUIT_HESSE = "HESSE" //HESSE errors
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant `MINUIT_ENGINE_NAME="MINUIT"`
|
||||||
|
*/
|
||||||
|
const val MINUIT_ENGINE_NAME = "MINUIT"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* 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.minuit
|
||||||
|
|
||||||
|
import hep.dataforge.context.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Мэнеджер для MINUITа. Пока не играет никакой активной роли кроме ведения
|
||||||
|
* внутреннего лога.
|
||||||
|
*
|
||||||
|
* @author Darksnake
|
||||||
|
* @version $Id: $Id
|
||||||
|
*/
|
||||||
|
@PluginDef(group = "hep.dataforge",
|
||||||
|
name = "MINUIT",
|
||||||
|
dependsOn = ["hep.dataforge:fitting"],
|
||||||
|
info = "The MINUIT fitter engine for DataForge fitting")
|
||||||
|
class MINUITPlugin : BasicPlugin() {
|
||||||
|
fun attach(@NotNull context: Context?) {
|
||||||
|
super.attach(context)
|
||||||
|
clearStaticLog()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides(Fitter.FITTER_TARGET)
|
||||||
|
fun getFitter(fitterName: String): Fitter? {
|
||||||
|
return if (fitterName == "MINUIT") {
|
||||||
|
MINUITFitter()
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ProvidesNames(Fitter.FITTER_TARGET)
|
||||||
|
fun listFitters(): List<String> {
|
||||||
|
return listOf("MINUIT")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun detach() {
|
||||||
|
clearStaticLog()
|
||||||
|
super.detach()
|
||||||
|
}
|
||||||
|
|
||||||
|
class Factory : PluginFactory() {
|
||||||
|
fun build(meta: Meta?): Plugin {
|
||||||
|
return MINUITPlugin()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getType(): java.lang.Class<out Plugin?> {
|
||||||
|
return MINUITPlugin::class.java
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Constant `staticLog`
|
||||||
|
*/
|
||||||
|
private val staticLog: Chronicle? = Chronicle("MINUIT-STATIC", Global.INSTANCE.getHistory())
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* clearStaticLog.
|
||||||
|
*/
|
||||||
|
fun clearStaticLog() {
|
||||||
|
staticLog.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* logStatic.
|
||||||
|
*
|
||||||
|
* @param str a [String] object.
|
||||||
|
* @param pars a [Object] object.
|
||||||
|
*/
|
||||||
|
fun logStatic(str: String?, vararg pars: Any?) {
|
||||||
|
checkNotNull(staticLog) { "MINUIT log is not initialized." }
|
||||||
|
staticLog.report(str, pars)
|
||||||
|
LoggerFactory.getLogger("MINUIT").info(String.format(str, *pars))
|
||||||
|
// Out.out.printf(str,pars);
|
||||||
|
// Out.out.println();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,121 @@
|
|||||||
|
/*
|
||||||
|
* 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.minuit
|
||||||
|
|
||||||
|
import hep.dataforge.MINUIT.FunctionMinimum
|
||||||
|
|
||||||
|
internal object MINUITUtils {
|
||||||
|
fun getFcn(source: FitState, allPar: ParamSet, fitPars: Array<String>): MultiFunction {
|
||||||
|
return MnFunc(source, allPar, fitPars)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFitParameters(set: ParamSet, fitPars: Array<String>): MnUserParameters {
|
||||||
|
val pars = MnUserParameters()
|
||||||
|
var i: Int
|
||||||
|
var par: Param
|
||||||
|
i = 0
|
||||||
|
while (i < fitPars.size) {
|
||||||
|
par = set.getByName(fitPars[i])
|
||||||
|
pars.add(fitPars[i], par.getValue(), par.getErr())
|
||||||
|
if (par.getLowerBound() > Double.NEGATIVE_INFINITY && par.getUpperBound() < Double.POSITIVE_INFINITY) {
|
||||||
|
pars.setLimits(i, par.getLowerBound(), par.getUpperBound())
|
||||||
|
} else if (par.getLowerBound() > Double.NEGATIVE_INFINITY) {
|
||||||
|
pars.setLowerLimit(i, par.getLowerBound())
|
||||||
|
} else if (par.getUpperBound() < Double.POSITIVE_INFINITY) {
|
||||||
|
pars.setUpperLimit(i, par.getUpperBound())
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return pars
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getValueSet(allPar: ParamSet, names: Array<String>, values: DoubleArray): ParamSet {
|
||||||
|
assert(values.size == names.size)
|
||||||
|
assert(allPar.getNames().contains(names))
|
||||||
|
val vector: ParamSet = allPar.copy()
|
||||||
|
for (i in values.indices) {
|
||||||
|
vector.setParValue(names[i], values[i])
|
||||||
|
}
|
||||||
|
return vector
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isValidArray(ar: DoubleArray): Boolean {
|
||||||
|
for (i in ar.indices) {
|
||||||
|
if (java.lang.Double.isNaN(ar[i])) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* printMINUITResult.
|
||||||
|
*
|
||||||
|
* @param out a [PrintWriter] object.
|
||||||
|
* @param minimum a [hep.dataforge.MINUIT.FunctionMinimum] object.
|
||||||
|
*/
|
||||||
|
fun printMINUITResult(out: PrintWriter, minimum: FunctionMinimum?) {
|
||||||
|
out.println()
|
||||||
|
out.println("***MINUIT INTERNAL FIT INFORMATION***")
|
||||||
|
out.println()
|
||||||
|
MnPrint.print(out, minimum)
|
||||||
|
out.println()
|
||||||
|
out.println("***END OF MINUIT INTERNAL FIT INFORMATION***")
|
||||||
|
out.println()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class MnFunc(source: FitState, allPar: ParamSet, fitPars: Array<String>) : MultiFunction {
|
||||||
|
var source: FitState
|
||||||
|
var allPar: ParamSet
|
||||||
|
var fitPars: Array<String>
|
||||||
|
fun value(doubles: DoubleArray): Double {
|
||||||
|
assert(isValidArray(doubles))
|
||||||
|
assert(doubles.size == fitPars.size)
|
||||||
|
return -2 * source.getLogProb(getValueSet(allPar, fitPars, doubles))
|
||||||
|
// source.getChi2(getValueSet(allPar, fitPars, doubles));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(NotDefinedException::class)
|
||||||
|
fun derivValue(n: Int, doubles: DoubleArray): Double {
|
||||||
|
assert(isValidArray(doubles))
|
||||||
|
assert(doubles.size == getDimension())
|
||||||
|
val set: ParamSet = getValueSet(allPar, fitPars, doubles)
|
||||||
|
|
||||||
|
// double res;
|
||||||
|
// double d, s, deriv;
|
||||||
|
//
|
||||||
|
// res = 0;
|
||||||
|
// for (int i = 0; i < source.getDataNum(); i++) {
|
||||||
|
// d = source.getDis(i, set);
|
||||||
|
// s = source.getDispersion(i, set);
|
||||||
|
// if (source.modelProvidesDerivs(fitPars[n])) {
|
||||||
|
// deriv = source.getDisDeriv(fitPars[n], i, set);
|
||||||
|
// } else {
|
||||||
|
// throw new NotDefinedException();
|
||||||
|
// // Такого не должно быть, поскольку мы где-то наверху должы были проверить, что производные все есть.
|
||||||
|
// }
|
||||||
|
// res += 2 * d * deriv / s;
|
||||||
|
// }
|
||||||
|
return -2 * source.getLogProbDeriv(fitPars[n], set)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getDimension(): Int {
|
||||||
|
return fitPars.size
|
||||||
|
}
|
||||||
|
|
||||||
|
fun providesDeriv(n: Int): Boolean {
|
||||||
|
return source.modelProvidesDerivs(fitPars[n])
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
this.source = source
|
||||||
|
this.allPar = allPar
|
||||||
|
this.fitPars = fitPars
|
||||||
|
assert(source.getModel().getNames().contains(fitPars))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
interface MinimumBuilder {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* minimum.
|
||||||
|
*
|
||||||
|
* @param fcn a [hep.dataforge.MINUIT.MnFcn] object.
|
||||||
|
* @param gc a [hep.dataforge.MINUIT.GradientCalculator] object.
|
||||||
|
* @param seed a [hep.dataforge.MINUIT.MinimumSeed] object.
|
||||||
|
* @param strategy a [hep.dataforge.MINUIT.MnStrategy] object.
|
||||||
|
* @param maxfcn a int.
|
||||||
|
* @param toler a double.
|
||||||
|
* @return a [hep.dataforge.MINUIT.FunctionMinimum] object.
|
||||||
|
*/
|
||||||
|
fun minimum(
|
||||||
|
fcn: MnFcn?,
|
||||||
|
gc: GradientCalculator?,
|
||||||
|
seed: MinimumSeed?,
|
||||||
|
strategy: MnStrategy?,
|
||||||
|
maxfcn: Int,
|
||||||
|
toler: Double
|
||||||
|
): FunctionMinimum
|
||||||
|
}
|
@ -0,0 +1,155 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import space.kscience.kmath.optimization.minuit.MINUITPlugin
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MinimumError keeps the inverse 2nd derivative (inverse Hessian) used for
|
||||||
|
* calculating the parameter step size (-V*g) and for the covariance update
|
||||||
|
* (ErrorUpdator). The covariance matrix is equal to twice the inverse Hessian.
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
class MinimumError {
|
||||||
|
private var theAvailable = false
|
||||||
|
private var theDCovar: Double
|
||||||
|
private var theHesseFailed = false
|
||||||
|
private var theInvertFailed = false
|
||||||
|
private var theMadePosDef = false
|
||||||
|
private var theMatrix: MnAlgebraicSymMatrix
|
||||||
|
private var thePosDef = false
|
||||||
|
private var theValid = false
|
||||||
|
|
||||||
|
constructor(n: Int) {
|
||||||
|
theMatrix = MnAlgebraicSymMatrix(n)
|
||||||
|
theDCovar = 1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(mat: MnAlgebraicSymMatrix, dcov: Double) {
|
||||||
|
theMatrix = mat
|
||||||
|
theDCovar = dcov
|
||||||
|
theValid = true
|
||||||
|
thePosDef = true
|
||||||
|
theAvailable = true
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(mat: MnAlgebraicSymMatrix, x: MnHesseFailed?) {
|
||||||
|
theMatrix = mat
|
||||||
|
theDCovar = 1.0
|
||||||
|
theValid = false
|
||||||
|
thePosDef = false
|
||||||
|
theMadePosDef = false
|
||||||
|
theHesseFailed = true
|
||||||
|
theInvertFailed = false
|
||||||
|
theAvailable = true
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(mat: MnAlgebraicSymMatrix, x: MnMadePosDef?) {
|
||||||
|
theMatrix = mat
|
||||||
|
theDCovar = 1.0
|
||||||
|
theValid = false
|
||||||
|
thePosDef = false
|
||||||
|
theMadePosDef = true
|
||||||
|
theHesseFailed = false
|
||||||
|
theInvertFailed = false
|
||||||
|
theAvailable = true
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(mat: MnAlgebraicSymMatrix, x: MnInvertFailed?) {
|
||||||
|
theMatrix = mat
|
||||||
|
theDCovar = 1.0
|
||||||
|
theValid = false
|
||||||
|
thePosDef = true
|
||||||
|
theMadePosDef = false
|
||||||
|
theHesseFailed = false
|
||||||
|
theInvertFailed = true
|
||||||
|
theAvailable = true
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(mat: MnAlgebraicSymMatrix, x: MnNotPosDef?) {
|
||||||
|
theMatrix = mat
|
||||||
|
theDCovar = 1.0
|
||||||
|
theValid = false
|
||||||
|
thePosDef = false
|
||||||
|
theMadePosDef = false
|
||||||
|
theHesseFailed = false
|
||||||
|
theInvertFailed = false
|
||||||
|
theAvailable = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun dcovar(): Double {
|
||||||
|
return theDCovar
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hesseFailed(): Boolean {
|
||||||
|
return theHesseFailed
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hessian(): MnAlgebraicSymMatrix {
|
||||||
|
return try {
|
||||||
|
val tmp: MnAlgebraicSymMatrix = theMatrix.copy()
|
||||||
|
tmp.invert()
|
||||||
|
tmp
|
||||||
|
} catch (x: SingularMatrixException) {
|
||||||
|
MINUITPlugin.logStatic("BasicMinimumError inversion fails; return diagonal matrix.")
|
||||||
|
val tmp = MnAlgebraicSymMatrix(theMatrix.nrow())
|
||||||
|
var i = 0
|
||||||
|
while (i < theMatrix.nrow()) {
|
||||||
|
tmp[i, i] = 1.0 / theMatrix[i, i]
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
tmp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun invHessian(): MnAlgebraicSymMatrix {
|
||||||
|
return theMatrix
|
||||||
|
}
|
||||||
|
|
||||||
|
fun invertFailed(): Boolean {
|
||||||
|
return theInvertFailed
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isAccurate(): Boolean {
|
||||||
|
return theDCovar < 0.1
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isAvailable(): Boolean {
|
||||||
|
return theAvailable
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isMadePosDef(): Boolean {
|
||||||
|
return theMadePosDef
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isPosDef(): Boolean {
|
||||||
|
return thePosDef
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isValid(): Boolean {
|
||||||
|
return theValid
|
||||||
|
}
|
||||||
|
|
||||||
|
fun matrix(): MnAlgebraicSymMatrix {
|
||||||
|
return MnUtils.mul(theMatrix, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class MnHesseFailed
|
||||||
|
internal class MnInvertFailed
|
||||||
|
internal class MnMadePosDef
|
||||||
|
internal class MnNotPosDef
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
internal interface MinimumErrorUpdator {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* update.
|
||||||
|
*
|
||||||
|
* @param state a [hep.dataforge.MINUIT.MinimumState] object.
|
||||||
|
* @param par a [hep.dataforge.MINUIT.MinimumParameters] object.
|
||||||
|
* @param grad a [hep.dataforge.MINUIT.FunctionGradient] object.
|
||||||
|
* @return a [hep.dataforge.MINUIT.MinimumError] object.
|
||||||
|
*/
|
||||||
|
fun update(state: MinimumState?, par: MinimumParameters?, grad: FunctionGradient?): MinimumError?
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import org.apache.commons.math3.linear.ArrayRealVector
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
class MinimumParameters {
|
||||||
|
private var theFVal = 0.0
|
||||||
|
private var theHasStep = false
|
||||||
|
private var theParameters: RealVector
|
||||||
|
private var theStepSize: RealVector
|
||||||
|
private var theValid = false
|
||||||
|
|
||||||
|
constructor(n: Int) {
|
||||||
|
theParameters = ArrayRealVector(n)
|
||||||
|
theStepSize = ArrayRealVector(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(avec: RealVector, fval: Double) {
|
||||||
|
theParameters = avec
|
||||||
|
theStepSize = ArrayRealVector(avec.getDimension())
|
||||||
|
theFVal = fval
|
||||||
|
theValid = true
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(avec: RealVector, dirin: RealVector, fval: Double) {
|
||||||
|
theParameters = avec
|
||||||
|
theStepSize = dirin
|
||||||
|
theFVal = fval
|
||||||
|
theValid = true
|
||||||
|
theHasStep = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun dirin(): RealVector {
|
||||||
|
return theStepSize
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fval(): Double {
|
||||||
|
return theFVal
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasStepSize(): Boolean {
|
||||||
|
return theHasStep
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isValid(): Boolean {
|
||||||
|
return theValid
|
||||||
|
}
|
||||||
|
|
||||||
|
fun vec(): RealVector {
|
||||||
|
return theParameters
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
class MinimumSeed(state: MinimumState, trafo: MnUserTransformation) {
|
||||||
|
private val theState: MinimumState = state
|
||||||
|
private val theTrafo: MnUserTransformation = trafo
|
||||||
|
private val theValid: Boolean = true
|
||||||
|
val edm: Double get() = state().edm()
|
||||||
|
|
||||||
|
fun error(): MinimumError {
|
||||||
|
return state().error()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fval(): Double {
|
||||||
|
return state().fval()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun gradient(): FunctionGradient {
|
||||||
|
return state().gradient()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isValid(): Boolean {
|
||||||
|
return theValid
|
||||||
|
}
|
||||||
|
|
||||||
|
fun nfcn(): Int {
|
||||||
|
return state().nfcn()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun parameters(): MinimumParameters {
|
||||||
|
return state().parameters()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun precision(): MnMachinePrecision {
|
||||||
|
return theTrafo.precision()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun state(): MinimumState {
|
||||||
|
return theState
|
||||||
|
}
|
||||||
|
|
||||||
|
fun trafo(): MnUserTransformation {
|
||||||
|
return theTrafo
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
/**
|
||||||
|
* base class for seed generators (starting values); the seed generator prepares
|
||||||
|
* initial starting values from the input (MnUserParameterState) for the
|
||||||
|
* minimization;
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
interface MinimumSeedGenerator {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* generate.
|
||||||
|
*
|
||||||
|
* @param fcn a [hep.dataforge.MINUIT.MnFcn] object.
|
||||||
|
* @param calc a [hep.dataforge.MINUIT.GradientCalculator] object.
|
||||||
|
* @param user a [hep.dataforge.MINUIT.MnUserParameterState] object.
|
||||||
|
* @param stra a [hep.dataforge.MINUIT.MnStrategy] object.
|
||||||
|
* @return a [hep.dataforge.MINUIT.MinimumSeed] object.
|
||||||
|
*/
|
||||||
|
fun generate(fcn: MnFcn?, calc: GradientCalculator?, user: MnUserParameterState?, stra: MnStrategy?): MinimumSeed
|
||||||
|
}
|
@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import org.apache.commons.math3.linear.RealVector
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MinimumState keeps the information (position, gradient, 2nd deriv, etc) after
|
||||||
|
* one minimization step (usually in MinimumBuilder).
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
class MinimumState {
|
||||||
|
private var theEDM = 0.0
|
||||||
|
private var theError: MinimumError
|
||||||
|
private var theGradient: FunctionGradient
|
||||||
|
private var theNFcn = 0
|
||||||
|
private var theParameters: MinimumParameters
|
||||||
|
|
||||||
|
constructor(n: Int) {
|
||||||
|
theParameters = MinimumParameters(n)
|
||||||
|
theError = MinimumError(n)
|
||||||
|
theGradient = FunctionGradient(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(states: MinimumParameters, err: MinimumError, grad: FunctionGradient, edm: Double, nfcn: Int) {
|
||||||
|
theParameters = states
|
||||||
|
theError = err
|
||||||
|
theGradient = grad
|
||||||
|
theEDM = edm
|
||||||
|
theNFcn = nfcn
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(states: MinimumParameters, edm: Double, nfcn: Int) {
|
||||||
|
theParameters = states
|
||||||
|
theError = MinimumError(states.vec().getDimension())
|
||||||
|
theGradient = FunctionGradient(states.vec().getDimension())
|
||||||
|
theEDM = edm
|
||||||
|
theNFcn = nfcn
|
||||||
|
}
|
||||||
|
|
||||||
|
fun edm(): Double {
|
||||||
|
return theEDM
|
||||||
|
}
|
||||||
|
|
||||||
|
fun error(): MinimumError {
|
||||||
|
return theError
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fval(): Double {
|
||||||
|
return theParameters.fval()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun gradient(): FunctionGradient {
|
||||||
|
return theGradient
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasCovariance(): Boolean {
|
||||||
|
return theError.isAvailable()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasParameters(): Boolean {
|
||||||
|
return theParameters.isValid()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isValid(): Boolean {
|
||||||
|
return if (hasParameters() && hasCovariance()) {
|
||||||
|
parameters().isValid() && error().isValid()
|
||||||
|
} else if (hasParameters()) {
|
||||||
|
parameters().isValid()
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun nfcn(): Int {
|
||||||
|
return theNFcn
|
||||||
|
}
|
||||||
|
|
||||||
|
fun parameters(): MinimumParameters {
|
||||||
|
return theParameters
|
||||||
|
}
|
||||||
|
|
||||||
|
fun size(): Int {
|
||||||
|
return theParameters.vec().getDimension()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun vec(): RealVector {
|
||||||
|
return theParameters.vec()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,219 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* MinosError class.
|
||||||
|
*
|
||||||
|
* @author Darksnake
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
class MinosError {
|
||||||
|
private var theLower: MnCross
|
||||||
|
private var theMinValue = 0.0
|
||||||
|
private var theParameter = 0
|
||||||
|
private var theUpper: MnCross
|
||||||
|
|
||||||
|
internal constructor() {
|
||||||
|
theUpper = MnCross()
|
||||||
|
theLower = MnCross()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal constructor(par: Int, min: Double, low: MnCross, up: MnCross) {
|
||||||
|
theParameter = par
|
||||||
|
theMinValue = min
|
||||||
|
theUpper = up
|
||||||
|
theLower = low
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* atLowerLimit.
|
||||||
|
*
|
||||||
|
* @return a boolean.
|
||||||
|
*/
|
||||||
|
fun atLowerLimit(): Boolean {
|
||||||
|
return theLower.atLimit()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* atLowerMaxFcn.
|
||||||
|
*
|
||||||
|
* @return a boolean.
|
||||||
|
*/
|
||||||
|
fun atLowerMaxFcn(): Boolean {
|
||||||
|
return theLower.atMaxFcn()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* atUpperLimit.
|
||||||
|
*
|
||||||
|
* @return a boolean.
|
||||||
|
*/
|
||||||
|
fun atUpperLimit(): Boolean {
|
||||||
|
return theUpper.atLimit()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* atUpperMaxFcn.
|
||||||
|
*
|
||||||
|
* @return a boolean.
|
||||||
|
*/
|
||||||
|
fun atUpperMaxFcn(): Boolean {
|
||||||
|
return theUpper.atMaxFcn()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* isValid.
|
||||||
|
*
|
||||||
|
* @return a boolean.
|
||||||
|
*/
|
||||||
|
fun isValid(): Boolean {
|
||||||
|
return theLower.isValid() && theUpper.isValid()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* lower.
|
||||||
|
*
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
fun lower(): Double {
|
||||||
|
return -1.0 * lowerState().error(parameter()) * (1.0 + theLower.value())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* lowerNewMin.
|
||||||
|
*
|
||||||
|
* @return a boolean.
|
||||||
|
*/
|
||||||
|
fun lowerNewMin(): Boolean {
|
||||||
|
return theLower.newMinimum()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* lowerState.
|
||||||
|
*
|
||||||
|
* @return a [hep.dataforge.MINUIT.MnUserParameterState] object.
|
||||||
|
*/
|
||||||
|
fun lowerState(): MnUserParameterState {
|
||||||
|
return theLower.state()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* lowerValid.
|
||||||
|
*
|
||||||
|
* @return a boolean.
|
||||||
|
*/
|
||||||
|
fun lowerValid(): Boolean {
|
||||||
|
return theLower.isValid()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* min.
|
||||||
|
*
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
fun min(): Double {
|
||||||
|
return theMinValue
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* nfcn.
|
||||||
|
*
|
||||||
|
* @return a int.
|
||||||
|
*/
|
||||||
|
fun nfcn(): Int {
|
||||||
|
return theUpper.nfcn() + theLower.nfcn()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* parameter.
|
||||||
|
*
|
||||||
|
* @return a int.
|
||||||
|
*/
|
||||||
|
fun parameter(): Int {
|
||||||
|
return theParameter
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* range.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
fun range(): Range {
|
||||||
|
return Range(lower(), upper())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
override fun toString(): String {
|
||||||
|
return MnPrint.toString(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* upper.
|
||||||
|
*
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
fun upper(): Double {
|
||||||
|
return upperState().error(parameter()) * (1.0 + theUpper.value())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* upperNewMin.
|
||||||
|
*
|
||||||
|
* @return a boolean.
|
||||||
|
*/
|
||||||
|
fun upperNewMin(): Boolean {
|
||||||
|
return theUpper.newMinimum()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* upperState.
|
||||||
|
*
|
||||||
|
* @return a [hep.dataforge.MINUIT.MnUserParameterState] object.
|
||||||
|
*/
|
||||||
|
fun upperState(): MnUserParameterState {
|
||||||
|
return theUpper.state()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* upperValid.
|
||||||
|
*
|
||||||
|
* @return a boolean.
|
||||||
|
*/
|
||||||
|
fun upperValid(): Boolean {
|
||||||
|
return theUpper.isValid()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,314 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
class MinuitParameter {
|
||||||
|
private var theConst = false
|
||||||
|
private var theError = 0.0
|
||||||
|
private var theFix = false
|
||||||
|
private var theLoLimValid = false
|
||||||
|
private var theLoLimit = 0.0
|
||||||
|
private var theName: String
|
||||||
|
private var theNum: Int
|
||||||
|
private var theUpLimValid = false
|
||||||
|
private var theUpLimit = 0.0
|
||||||
|
private var theValue: Double
|
||||||
|
|
||||||
|
/**
|
||||||
|
* constructor for constant parameter
|
||||||
|
*
|
||||||
|
* @param num a int.
|
||||||
|
* @param name a [String] object.
|
||||||
|
* @param val a double.
|
||||||
|
*/
|
||||||
|
constructor(num: Int, name: String, `val`: Double) {
|
||||||
|
theNum = num
|
||||||
|
theValue = `val`
|
||||||
|
theConst = true
|
||||||
|
theName = name
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* constructor for standard parameter
|
||||||
|
*
|
||||||
|
* @param num a int.
|
||||||
|
* @param name a [String] object.
|
||||||
|
* @param val a double.
|
||||||
|
* @param err a double.
|
||||||
|
*/
|
||||||
|
constructor(num: Int, name: String, `val`: Double, err: Double) {
|
||||||
|
theNum = num
|
||||||
|
theValue = `val`
|
||||||
|
theError = err
|
||||||
|
theName = name
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* constructor for limited parameter
|
||||||
|
*
|
||||||
|
* @param num a int.
|
||||||
|
* @param name a [String] object.
|
||||||
|
* @param val a double.
|
||||||
|
* @param err a double.
|
||||||
|
* @param min a double.
|
||||||
|
* @param max a double.
|
||||||
|
*/
|
||||||
|
constructor(num: Int, name: String, `val`: Double, err: Double, min: Double, max: Double) {
|
||||||
|
theNum = num
|
||||||
|
theValue = `val`
|
||||||
|
theError = err
|
||||||
|
theLoLimit = min
|
||||||
|
theUpLimit = max
|
||||||
|
theLoLimValid = true
|
||||||
|
theUpLimValid = true
|
||||||
|
require(min != max) { "min == max" }
|
||||||
|
if (min > max) {
|
||||||
|
theLoLimit = max
|
||||||
|
theUpLimit = min
|
||||||
|
}
|
||||||
|
theName = name
|
||||||
|
}
|
||||||
|
|
||||||
|
private constructor(other: MinuitParameter) {
|
||||||
|
theNum = other.theNum
|
||||||
|
theName = other.theName
|
||||||
|
theValue = other.theValue
|
||||||
|
theError = other.theError
|
||||||
|
theConst = other.theConst
|
||||||
|
theFix = other.theFix
|
||||||
|
theLoLimit = other.theLoLimit
|
||||||
|
theUpLimit = other.theUpLimit
|
||||||
|
theLoLimValid = other.theLoLimValid
|
||||||
|
theUpLimValid = other.theUpLimValid
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* copy.
|
||||||
|
*
|
||||||
|
* @return a [hep.dataforge.MINUIT.MinuitParameter] object.
|
||||||
|
*/
|
||||||
|
fun copy(): MinuitParameter {
|
||||||
|
return MinuitParameter(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* error.
|
||||||
|
*
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
fun error(): Double {
|
||||||
|
return theError
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* fix.
|
||||||
|
*/
|
||||||
|
fun fix() {
|
||||||
|
theFix = true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* hasLimits.
|
||||||
|
*
|
||||||
|
* @return a boolean.
|
||||||
|
*/
|
||||||
|
fun hasLimits(): Boolean {
|
||||||
|
return theLoLimValid || theUpLimValid
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* hasLowerLimit.
|
||||||
|
*
|
||||||
|
* @return a boolean.
|
||||||
|
*/
|
||||||
|
fun hasLowerLimit(): Boolean {
|
||||||
|
return theLoLimValid
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* hasUpperLimit.
|
||||||
|
*
|
||||||
|
* @return a boolean.
|
||||||
|
*/
|
||||||
|
fun hasUpperLimit(): Boolean {
|
||||||
|
return theUpLimValid
|
||||||
|
}
|
||||||
|
//state of parameter (fixed/const/limited)
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* isConst.
|
||||||
|
*
|
||||||
|
* @return a boolean.
|
||||||
|
*/
|
||||||
|
fun isConst(): Boolean {
|
||||||
|
return theConst
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* isFixed.
|
||||||
|
*
|
||||||
|
* @return a boolean.
|
||||||
|
*/
|
||||||
|
fun isFixed(): Boolean {
|
||||||
|
return theFix
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* lowerLimit.
|
||||||
|
*
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
fun lowerLimit(): Double {
|
||||||
|
return theLoLimit
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* name.
|
||||||
|
*
|
||||||
|
* @return a [String] object.
|
||||||
|
*/
|
||||||
|
fun name(): String {
|
||||||
|
return theName
|
||||||
|
}
|
||||||
|
//access methods
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* number.
|
||||||
|
*
|
||||||
|
* @return a int.
|
||||||
|
*/
|
||||||
|
fun number(): Int {
|
||||||
|
return theNum
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* release.
|
||||||
|
*/
|
||||||
|
fun release() {
|
||||||
|
theFix = false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* removeLimits.
|
||||||
|
*/
|
||||||
|
fun removeLimits() {
|
||||||
|
theLoLimit = 0.0
|
||||||
|
theUpLimit = 0.0
|
||||||
|
theLoLimValid = false
|
||||||
|
theUpLimValid = false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setError.
|
||||||
|
*
|
||||||
|
* @param err a double.
|
||||||
|
*/
|
||||||
|
fun setError(err: Double) {
|
||||||
|
theError = err
|
||||||
|
theConst = false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setLimits.
|
||||||
|
*
|
||||||
|
* @param low a double.
|
||||||
|
* @param up a double.
|
||||||
|
*/
|
||||||
|
fun setLimits(low: Double, up: Double) {
|
||||||
|
require(low != up) { "min == max" }
|
||||||
|
theLoLimit = low
|
||||||
|
theUpLimit = up
|
||||||
|
theLoLimValid = true
|
||||||
|
theUpLimValid = true
|
||||||
|
if (low > up) {
|
||||||
|
theLoLimit = up
|
||||||
|
theUpLimit = low
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setLowerLimit.
|
||||||
|
*
|
||||||
|
* @param low a double.
|
||||||
|
*/
|
||||||
|
fun setLowerLimit(low: Double) {
|
||||||
|
theLoLimit = low
|
||||||
|
theUpLimit = 0.0
|
||||||
|
theLoLimValid = true
|
||||||
|
theUpLimValid = false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setUpperLimit.
|
||||||
|
*
|
||||||
|
* @param up a double.
|
||||||
|
*/
|
||||||
|
fun setUpperLimit(up: Double) {
|
||||||
|
theLoLimit = 0.0
|
||||||
|
theUpLimit = up
|
||||||
|
theLoLimValid = false
|
||||||
|
theUpLimValid = true
|
||||||
|
}
|
||||||
|
//interaction
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setValue.
|
||||||
|
*
|
||||||
|
* @param val a double.
|
||||||
|
*/
|
||||||
|
fun setValue(`val`: Double) {
|
||||||
|
theValue = `val`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* upperLimit.
|
||||||
|
*
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
fun upperLimit(): Double {
|
||||||
|
return theUpLimit
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* value.
|
||||||
|
*
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
fun value(): Double {
|
||||||
|
return theValue
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,458 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import org.apache.commons.math3.linear.ArrayRealVector
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
class MnAlgebraicSymMatrix(n: Int) {
|
||||||
|
private val theData: DoubleArray
|
||||||
|
private val theNRow: Int
|
||||||
|
private val theSize: Int
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* copy.
|
||||||
|
*
|
||||||
|
* @return a [hep.dataforge.MINUIT.MnAlgebraicSymMatrix] object.
|
||||||
|
*/
|
||||||
|
fun copy(): MnAlgebraicSymMatrix {
|
||||||
|
val copy = MnAlgebraicSymMatrix(theNRow)
|
||||||
|
java.lang.System.arraycopy(theData, 0, copy.theData, 0, theSize)
|
||||||
|
return copy
|
||||||
|
}
|
||||||
|
|
||||||
|
fun data(): DoubleArray {
|
||||||
|
return theData
|
||||||
|
}
|
||||||
|
|
||||||
|
fun eigenvalues(): ArrayRealVector {
|
||||||
|
val nrow = theNRow
|
||||||
|
val tmp = DoubleArray((nrow + 1) * (nrow + 1))
|
||||||
|
val work = DoubleArray(1 + 2 * nrow)
|
||||||
|
for (i in 0 until nrow) {
|
||||||
|
for (j in 0..i) {
|
||||||
|
tmp[1 + i + (1 + j) * nrow] = get(i, j)
|
||||||
|
tmp[(1 + i) * nrow + (1 + j)] = get(i, j)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val info = mneigen(tmp, nrow, nrow, work.size, work, 1e-6)
|
||||||
|
if (info != 0) {
|
||||||
|
throw EigenvaluesException()
|
||||||
|
}
|
||||||
|
val result = ArrayRealVector(nrow)
|
||||||
|
for (i in 0 until nrow) {
|
||||||
|
result.setEntry(i, work[1 + i])
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun get(row: Int, col: Int): Double {
|
||||||
|
if (row >= theNRow || col >= theNRow) {
|
||||||
|
throw ArrayIndexOutOfBoundsException()
|
||||||
|
}
|
||||||
|
return theData[theIndex(row, col)]
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(SingularMatrixException::class)
|
||||||
|
fun invert() {
|
||||||
|
if (theSize == 1) {
|
||||||
|
val tmp = theData[0]
|
||||||
|
if (tmp <= 0.0) {
|
||||||
|
throw SingularMatrixException()
|
||||||
|
}
|
||||||
|
theData[0] = 1.0 / tmp
|
||||||
|
} else {
|
||||||
|
val nrow = theNRow
|
||||||
|
val s = DoubleArray(nrow)
|
||||||
|
val q = DoubleArray(nrow)
|
||||||
|
val pp = DoubleArray(nrow)
|
||||||
|
for (i in 0 until nrow) {
|
||||||
|
val si = theData[theIndex(i, i)]
|
||||||
|
if (si < 0.0) {
|
||||||
|
throw SingularMatrixException()
|
||||||
|
}
|
||||||
|
s[i] = 1.0 / sqrt(si)
|
||||||
|
}
|
||||||
|
for (i in 0 until nrow) {
|
||||||
|
for (j in i until nrow) {
|
||||||
|
theData[theIndex(i, j)] *= s[i] * s[j]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i in 0 until nrow) {
|
||||||
|
var k = i
|
||||||
|
if (theData[theIndex(k, k)] == 0.0) {
|
||||||
|
throw SingularMatrixException()
|
||||||
|
}
|
||||||
|
q[k] = 1.0 / theData[theIndex(k, k)]
|
||||||
|
pp[k] = 1.0
|
||||||
|
theData[theIndex(k, k)] = 0.0
|
||||||
|
val kp1 = k + 1
|
||||||
|
if (k != 0) {
|
||||||
|
for (j in 0 until k) {
|
||||||
|
val index = theIndex(j, k)
|
||||||
|
pp[j] = theData[index]
|
||||||
|
q[j] = theData[index] * q[k]
|
||||||
|
theData[index] = 0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (k != nrow - 1) {
|
||||||
|
for (j in kp1 until nrow) {
|
||||||
|
val index = theIndex(k, j)
|
||||||
|
pp[j] = theData[index]
|
||||||
|
q[j] = -theData[index] * q[k]
|
||||||
|
theData[index] = 0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (j in 0 until nrow) {
|
||||||
|
k = j
|
||||||
|
while (k < nrow) {
|
||||||
|
theData[theIndex(j, k)] += pp[j] * q[k]
|
||||||
|
k++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (j in 0 until nrow) {
|
||||||
|
for (k in j until nrow) {
|
||||||
|
theData[theIndex(j, k)] *= s[j] * s[k]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ncol(): Int {
|
||||||
|
return nrow()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun nrow(): Int {
|
||||||
|
return theNRow
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun set(row: Int, col: Int, value: Double) {
|
||||||
|
if (row >= theNRow || col >= theNRow) {
|
||||||
|
throw ArrayIndexOutOfBoundsException()
|
||||||
|
}
|
||||||
|
theData[theIndex(row, col)] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun size(): Int {
|
||||||
|
return theSize
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun theIndex(row: Int, col: Int): Int {
|
||||||
|
return if (row > col) {
|
||||||
|
col + row * (row + 1) / 2
|
||||||
|
} else {
|
||||||
|
row + col * (col + 1) / 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
override fun toString(): String {
|
||||||
|
return MnPrint.toString(this)
|
||||||
|
} /* mneig_ */
|
||||||
|
|
||||||
|
private inner class EigenvaluesException : RuntimeException()
|
||||||
|
companion object {
|
||||||
|
private fun mneigen(a: DoubleArray, ndima: Int, n: Int, mits: Int, work: DoubleArray, precis: Double): Int {
|
||||||
|
|
||||||
|
/* System generated locals */
|
||||||
|
var i__2: Int
|
||||||
|
var i__3: Int
|
||||||
|
|
||||||
|
/* Local variables */
|
||||||
|
var b: Double
|
||||||
|
var c__: Double
|
||||||
|
var f: Double
|
||||||
|
var h__: Double
|
||||||
|
var i__: Int
|
||||||
|
var j: Int
|
||||||
|
var k: Int
|
||||||
|
var l: Int
|
||||||
|
var m = 0
|
||||||
|
var r__: Double
|
||||||
|
var s: Double
|
||||||
|
var i0: Int
|
||||||
|
var i1: Int
|
||||||
|
var j1: Int
|
||||||
|
var m1: Int
|
||||||
|
var hh: Double
|
||||||
|
var gl: Double
|
||||||
|
var pr: Double
|
||||||
|
var pt: Double
|
||||||
|
|
||||||
|
/* PRECIS is the machine precision EPSMAC */
|
||||||
|
/* Parameter adjustments */
|
||||||
|
val a_dim1: Int = ndima
|
||||||
|
val a_offset: Int = 1 + a_dim1 * 1
|
||||||
|
|
||||||
|
/* Function Body */
|
||||||
|
var ifault = 1
|
||||||
|
i__ = n
|
||||||
|
var i__1: Int = n
|
||||||
|
i1 = 2
|
||||||
|
while (i1 <= i__1) {
|
||||||
|
l = i__ - 2
|
||||||
|
f = a[i__ + (i__ - 1) * a_dim1]
|
||||||
|
gl = 0.0
|
||||||
|
if (l >= 1) {
|
||||||
|
i__2 = l
|
||||||
|
k = 1
|
||||||
|
while (k <= i__2) {
|
||||||
|
|
||||||
|
/* Computing 2nd power */
|
||||||
|
val r__1 = a[i__ + k * a_dim1]
|
||||||
|
gl += r__1 * r__1
|
||||||
|
++k
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Computing 2nd power */h__ = gl + f * f
|
||||||
|
if (gl <= 1e-35) {
|
||||||
|
work[i__] = 0.0
|
||||||
|
work[n + i__] = f
|
||||||
|
} else {
|
||||||
|
++l
|
||||||
|
gl = sqrt(h__)
|
||||||
|
if (f >= 0.0) {
|
||||||
|
gl = -gl
|
||||||
|
}
|
||||||
|
work[n + i__] = gl
|
||||||
|
h__ -= f * gl
|
||||||
|
a[i__ + (i__ - 1) * a_dim1] = f - gl
|
||||||
|
f = 0.0
|
||||||
|
i__2 = l
|
||||||
|
j = 1
|
||||||
|
while (j <= i__2) {
|
||||||
|
a[j + i__ * a_dim1] = a[i__ + j * a_dim1] / h__
|
||||||
|
gl = 0.0
|
||||||
|
i__3 = j
|
||||||
|
k = 1
|
||||||
|
while (k <= i__3) {
|
||||||
|
gl += a[j + k * a_dim1] * a[i__ + k * a_dim1]
|
||||||
|
++k
|
||||||
|
}
|
||||||
|
if (j < l) {
|
||||||
|
j1 = j + 1
|
||||||
|
i__3 = l
|
||||||
|
k = j1
|
||||||
|
while (k <= i__3) {
|
||||||
|
gl += a[k + j * a_dim1] * a[i__ + k * a_dim1]
|
||||||
|
++k
|
||||||
|
}
|
||||||
|
}
|
||||||
|
work[n + j] = gl / h__
|
||||||
|
f += gl * a[j + i__ * a_dim1]
|
||||||
|
++j
|
||||||
|
}
|
||||||
|
hh = f / (h__ + h__)
|
||||||
|
i__2 = l
|
||||||
|
j = 1
|
||||||
|
while (j <= i__2) {
|
||||||
|
f = a[i__ + j * a_dim1]
|
||||||
|
gl = work[n + j] - hh * f
|
||||||
|
work[n + j] = gl
|
||||||
|
i__3 = j
|
||||||
|
k = 1
|
||||||
|
while (k <= i__3) {
|
||||||
|
a[j + k * a_dim1] = a[j + k * a_dim1] - f * work[n + k] - (gl
|
||||||
|
* a[i__ + k * a_dim1])
|
||||||
|
++k
|
||||||
|
}
|
||||||
|
++j
|
||||||
|
}
|
||||||
|
work[i__] = h__
|
||||||
|
}
|
||||||
|
--i__
|
||||||
|
++i1
|
||||||
|
}
|
||||||
|
work[1] = 0.0
|
||||||
|
work[n + 1] = 0.0
|
||||||
|
i__1 = n
|
||||||
|
i__ = 1
|
||||||
|
while (i__ <= i__1) {
|
||||||
|
l = i__ - 1
|
||||||
|
if (work[i__] != 0.0 && l != 0) {
|
||||||
|
i__3 = l
|
||||||
|
j = 1
|
||||||
|
while (j <= i__3) {
|
||||||
|
gl = 0.0
|
||||||
|
i__2 = l
|
||||||
|
k = 1
|
||||||
|
while (k <= i__2) {
|
||||||
|
gl += a[i__ + k * a_dim1] * a[k + j * a_dim1]
|
||||||
|
++k
|
||||||
|
}
|
||||||
|
i__2 = l
|
||||||
|
k = 1
|
||||||
|
while (k <= i__2) {
|
||||||
|
a[k + j * a_dim1] -= gl * a[k + i__ * a_dim1]
|
||||||
|
++k
|
||||||
|
}
|
||||||
|
++j
|
||||||
|
}
|
||||||
|
}
|
||||||
|
work[i__] = a[i__ + i__ * a_dim1]
|
||||||
|
a[i__ + i__ * a_dim1] = 1.0
|
||||||
|
if (l != 0) {
|
||||||
|
i__2 = l
|
||||||
|
j = 1
|
||||||
|
while (j <= i__2) {
|
||||||
|
a[i__ + j * a_dim1] = 0.0
|
||||||
|
a[j + i__ * a_dim1] = 0.0
|
||||||
|
++j
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++i__
|
||||||
|
}
|
||||||
|
val n1: Int = n - 1
|
||||||
|
i__1 = n
|
||||||
|
i__ = 2
|
||||||
|
while (i__ <= i__1) {
|
||||||
|
i0 = n + i__ - 1
|
||||||
|
work[i0] = work[i0 + 1]
|
||||||
|
++i__
|
||||||
|
}
|
||||||
|
work[n + n] = 0.0
|
||||||
|
b = 0.0
|
||||||
|
f = 0.0
|
||||||
|
i__1 = n
|
||||||
|
l = 1
|
||||||
|
while (l <= i__1) {
|
||||||
|
j = 0
|
||||||
|
h__ = precis * (abs(work[l]) + abs(work[n + l]))
|
||||||
|
if (b < h__) {
|
||||||
|
b = h__
|
||||||
|
}
|
||||||
|
i__2 = n
|
||||||
|
m1 = l
|
||||||
|
while (m1 <= i__2) {
|
||||||
|
m = m1
|
||||||
|
if (abs(work[n + m]) <= b) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
++m1
|
||||||
|
}
|
||||||
|
if (m != l) {
|
||||||
|
while (true) {
|
||||||
|
if (j == mits) {
|
||||||
|
return ifault
|
||||||
|
}
|
||||||
|
++j
|
||||||
|
pt = (work[l + 1] - work[l]) / (work[n + l] * 2.0)
|
||||||
|
r__ = sqrt(pt * pt + 1.0)
|
||||||
|
pr = pt + r__
|
||||||
|
if (pt < 0.0) {
|
||||||
|
pr = pt - r__
|
||||||
|
}
|
||||||
|
h__ = work[l] - work[n + l] / pr
|
||||||
|
i__2 = n
|
||||||
|
i__ = l
|
||||||
|
while (i__ <= i__2) {
|
||||||
|
work[i__] -= h__
|
||||||
|
++i__
|
||||||
|
}
|
||||||
|
f += h__
|
||||||
|
pt = work[m]
|
||||||
|
c__ = 1.0
|
||||||
|
s = 0.0
|
||||||
|
m1 = m - 1
|
||||||
|
i__ = m
|
||||||
|
i__2 = m1
|
||||||
|
i1 = l
|
||||||
|
while (i1 <= i__2) {
|
||||||
|
j = i__
|
||||||
|
--i__
|
||||||
|
gl = c__ * work[n + i__]
|
||||||
|
h__ = c__ * pt
|
||||||
|
if (abs(pt) < abs(work[n + i__])) {
|
||||||
|
c__ = pt / work[n + i__]
|
||||||
|
r__ = sqrt(c__ * c__ + 1.0)
|
||||||
|
work[n + j] = s * work[n + i__] * r__
|
||||||
|
s = 1.0 / r__
|
||||||
|
c__ /= r__
|
||||||
|
} else {
|
||||||
|
c__ = work[n + i__] / pt
|
||||||
|
r__ = sqrt(c__ * c__ + 1.0)
|
||||||
|
work[n + j] = s * pt * r__
|
||||||
|
s = c__ / r__
|
||||||
|
c__ = 1.0 / r__
|
||||||
|
}
|
||||||
|
pt = c__ * work[i__] - s * gl
|
||||||
|
work[j] = h__ + s * (c__ * gl + s * work[i__])
|
||||||
|
i__3 = n
|
||||||
|
k = 1
|
||||||
|
while (k <= i__3) {
|
||||||
|
h__ = a[k + j * a_dim1]
|
||||||
|
a[k + j * a_dim1] = s * a[k + i__ * a_dim1] + c__ * h__
|
||||||
|
a[k + i__ * a_dim1] = c__ * a[k + i__ * a_dim1] - s * h__
|
||||||
|
++k
|
||||||
|
}
|
||||||
|
++i1
|
||||||
|
}
|
||||||
|
work[n + l] = s * pt
|
||||||
|
work[l] = c__ * pt
|
||||||
|
if (abs(work[n + l]) <= b) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
work[l] += f
|
||||||
|
++l
|
||||||
|
}
|
||||||
|
i__1 = n1
|
||||||
|
i__ = 1
|
||||||
|
while (i__ <= i__1) {
|
||||||
|
k = i__
|
||||||
|
pt = work[i__]
|
||||||
|
i1 = i__ + 1
|
||||||
|
i__3 = n
|
||||||
|
j = i1
|
||||||
|
while (j <= i__3) {
|
||||||
|
if (work[j] < pt) {
|
||||||
|
k = j
|
||||||
|
pt = work[j]
|
||||||
|
}
|
||||||
|
++j
|
||||||
|
}
|
||||||
|
if (k != i__) {
|
||||||
|
work[k] = work[i__]
|
||||||
|
work[i__] = pt
|
||||||
|
i__3 = n
|
||||||
|
j = 1
|
||||||
|
while (j <= i__3) {
|
||||||
|
pt = a[j + i__ * a_dim1]
|
||||||
|
a[j + i__ * a_dim1] = a[j + k * a_dim1]
|
||||||
|
a[j + k * a_dim1] = pt
|
||||||
|
++j
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++i__
|
||||||
|
}
|
||||||
|
ifault = 0
|
||||||
|
return ifault
|
||||||
|
} /* mneig_ */
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
require(n >= 0) { "Invalid matrix size: $n" }
|
||||||
|
theSize = n * (n + 1) / 2
|
||||||
|
theNRow = n
|
||||||
|
theData = DoubleArray(theSize)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,554 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import ru.inr.mass.maths.MultiFunction
|
||||||
|
import ru.inr.mass.minuit.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for minimizers.
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
* @author Darksnake
|
||||||
|
*/
|
||||||
|
abstract class MnApplication {
|
||||||
|
/* package protected */
|
||||||
|
var checkAnalyticalDerivatives: Boolean
|
||||||
|
|
||||||
|
/* package protected */ /* package protected */
|
||||||
|
var theErrorDef = 1.0 /* package protected */
|
||||||
|
var theFCN: MultiFunction?
|
||||||
|
|
||||||
|
/* package protected */ /* package protected */
|
||||||
|
var theNumCall /* package protected */ = 0
|
||||||
|
var theState: MnUserParameterState
|
||||||
|
|
||||||
|
/* package protected */
|
||||||
|
var theStrategy: MnStrategy
|
||||||
|
|
||||||
|
/* package protected */
|
||||||
|
var useAnalyticalDerivatives: Boolean
|
||||||
|
|
||||||
|
/* package protected */
|
||||||
|
internal constructor(fcn: MultiFunction?, state: MnUserParameterState, stra: MnStrategy) {
|
||||||
|
theFCN = fcn
|
||||||
|
theState = state
|
||||||
|
theStrategy = stra
|
||||||
|
checkAnalyticalDerivatives = true
|
||||||
|
useAnalyticalDerivatives = true
|
||||||
|
}
|
||||||
|
|
||||||
|
internal constructor(fcn: MultiFunction?, state: MnUserParameterState, stra: MnStrategy, nfcn: Int) {
|
||||||
|
theFCN = fcn
|
||||||
|
theState = state
|
||||||
|
theStrategy = stra
|
||||||
|
theNumCall = nfcn
|
||||||
|
checkAnalyticalDerivatives = true
|
||||||
|
useAnalyticalDerivatives = true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* MultiFunction.
|
||||||
|
*
|
||||||
|
* @return a [MultiFunction] object.
|
||||||
|
*/
|
||||||
|
fun MultiFunction(): MultiFunction? {
|
||||||
|
return theFCN
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add free parameter
|
||||||
|
*
|
||||||
|
* @param err a double.
|
||||||
|
* @param val a double.
|
||||||
|
* @param name a [String] object.
|
||||||
|
*/
|
||||||
|
fun add(name: String, `val`: Double, err: Double) {
|
||||||
|
theState.add(name, `val`, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add limited parameter
|
||||||
|
*
|
||||||
|
* @param up a double.
|
||||||
|
* @param low a double.
|
||||||
|
* @param name a [String] object.
|
||||||
|
* @param val a double.
|
||||||
|
* @param err a double.
|
||||||
|
*/
|
||||||
|
fun add(name: String, `val`: Double, err: Double, low: Double, up: Double) {
|
||||||
|
theState.add(name, `val`, err, low, up)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add const parameter
|
||||||
|
*
|
||||||
|
* @param name a [String] object.
|
||||||
|
* @param val a double.
|
||||||
|
*/
|
||||||
|
fun add(name: String, `val`: Double) {
|
||||||
|
theState.add(name, `val`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* checkAnalyticalDerivatives.
|
||||||
|
*
|
||||||
|
* @return a boolean.
|
||||||
|
*/
|
||||||
|
fun checkAnalyticalDerivatives(): Boolean {
|
||||||
|
return checkAnalyticalDerivatives
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* covariance.
|
||||||
|
*
|
||||||
|
* @return a [hep.dataforge.MINUIT.MnUserCovariance] object.
|
||||||
|
*/
|
||||||
|
fun covariance(): MnUserCovariance {
|
||||||
|
return theState.covariance()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* error.
|
||||||
|
*
|
||||||
|
* @param index a int.
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
fun error(index: Int): Double {
|
||||||
|
return theState.error(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* error.
|
||||||
|
*
|
||||||
|
* @param name a [String] object.
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
fun error(name: String?): Double {
|
||||||
|
return theState.error(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* errorDef.
|
||||||
|
*
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
fun errorDef(): Double {
|
||||||
|
return theErrorDef
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* errors.
|
||||||
|
*
|
||||||
|
* @return an array of double.
|
||||||
|
*/
|
||||||
|
fun errors(): DoubleArray {
|
||||||
|
return theState.errors()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ext2int(i: Int, value: Double): Double {
|
||||||
|
return theState.ext2int(i, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun extOfInt(i: Int): Int {
|
||||||
|
return theState.extOfInt(i)
|
||||||
|
}
|
||||||
|
//interaction via external number of parameter
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* fix.
|
||||||
|
*
|
||||||
|
* @param index a int.
|
||||||
|
*/
|
||||||
|
fun fix(index: Int) {
|
||||||
|
theState.fix(index)
|
||||||
|
}
|
||||||
|
//interaction via name of parameter
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* fix.
|
||||||
|
*
|
||||||
|
* @param name a [String] object.
|
||||||
|
*/
|
||||||
|
fun fix(name: String?) {
|
||||||
|
theState.fix(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* convert name into external number of parameter
|
||||||
|
*
|
||||||
|
* @param name a [String] object.
|
||||||
|
* @return a int.
|
||||||
|
*/
|
||||||
|
fun index(name: String?): Int {
|
||||||
|
return theState.index(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// transformation internal <-> external
|
||||||
|
fun int2ext(i: Int, value: Double): Double {
|
||||||
|
return theState.int2ext(i, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun intOfExt(i: Int): Int {
|
||||||
|
return theState.intOfExt(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* minimize.
|
||||||
|
*
|
||||||
|
* @return a [hep.dataforge.MINUIT.FunctionMinimum] object.
|
||||||
|
*/
|
||||||
|
fun minimize(): FunctionMinimum {
|
||||||
|
return minimize(DEFAULT_MAXFCN)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* minimize.
|
||||||
|
*
|
||||||
|
* @param maxfcn a int.
|
||||||
|
* @return a [hep.dataforge.MINUIT.FunctionMinimum] object.
|
||||||
|
*/
|
||||||
|
fun minimize(maxfcn: Int): FunctionMinimum {
|
||||||
|
return minimize(maxfcn, DEFAULT_TOLER)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Causes minimization of the FCN and returns the result in form of a
|
||||||
|
* FunctionMinimum.
|
||||||
|
*
|
||||||
|
* @param maxfcn specifies the (approximate) maximum number of function
|
||||||
|
* calls after which the calculation will be stopped even if it has not yet
|
||||||
|
* converged.
|
||||||
|
* @param toler specifies the required tolerance on the function value at
|
||||||
|
* the minimum. The default tolerance value is 0.1, and the minimization
|
||||||
|
* will stop when the estimated vertical distance to the minimum (EDM) is
|
||||||
|
* less than 0:001*tolerance*errorDef
|
||||||
|
* @return a [hep.dataforge.MINUIT.FunctionMinimum] object.
|
||||||
|
*/
|
||||||
|
fun minimize(maxfcn: Int, toler: Double): FunctionMinimum {
|
||||||
|
var maxfcn = maxfcn
|
||||||
|
check(theState.isValid()) { "Invalid state" }
|
||||||
|
val npar = variableParameters()
|
||||||
|
if (maxfcn == 0) {
|
||||||
|
maxfcn = 200 + 100 * npar + 5 * npar * npar
|
||||||
|
}
|
||||||
|
val min: FunctionMinimum = minimizer().minimize(theFCN,
|
||||||
|
theState,
|
||||||
|
theStrategy,
|
||||||
|
maxfcn,
|
||||||
|
toler,
|
||||||
|
theErrorDef,
|
||||||
|
useAnalyticalDerivatives,
|
||||||
|
checkAnalyticalDerivatives)
|
||||||
|
theNumCall += min.nfcn()
|
||||||
|
theState = min.userState()
|
||||||
|
return min
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract fun minimizer(): ModularFunctionMinimizer
|
||||||
|
|
||||||
|
// facade: forward interface of MnUserParameters and MnUserTransformation
|
||||||
|
fun minuitParameters(): List<MinuitParameter> {
|
||||||
|
return theState.minuitParameters()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* convert external number into name of parameter
|
||||||
|
*
|
||||||
|
* @param index a int.
|
||||||
|
* @return a [String] object.
|
||||||
|
*/
|
||||||
|
fun name(index: Int): String {
|
||||||
|
return theState.name(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* numOfCalls.
|
||||||
|
*
|
||||||
|
* @return a int.
|
||||||
|
*/
|
||||||
|
fun numOfCalls(): Int {
|
||||||
|
return theNumCall
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* access to single parameter
|
||||||
|
* @param i
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
fun parameter(i: Int): MinuitParameter {
|
||||||
|
return theState.parameter(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* parameters.
|
||||||
|
*
|
||||||
|
* @return a [hep.dataforge.MINUIT.MnUserParameters] object.
|
||||||
|
*/
|
||||||
|
fun parameters(): MnUserParameters {
|
||||||
|
return theState.parameters()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* access to parameters and errors in column-wise representation
|
||||||
|
*
|
||||||
|
* @return an array of double.
|
||||||
|
*/
|
||||||
|
fun params(): DoubleArray {
|
||||||
|
return theState.params()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* precision.
|
||||||
|
*
|
||||||
|
* @return a [hep.dataforge.MINUIT.MnMachinePrecision] object.
|
||||||
|
*/
|
||||||
|
fun precision(): MnMachinePrecision {
|
||||||
|
return theState.precision()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* release.
|
||||||
|
*
|
||||||
|
* @param index a int.
|
||||||
|
*/
|
||||||
|
fun release(index: Int) {
|
||||||
|
theState.release(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* release.
|
||||||
|
*
|
||||||
|
* @param name a [String] object.
|
||||||
|
*/
|
||||||
|
fun release(name: String?) {
|
||||||
|
theState.release(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* removeLimits.
|
||||||
|
*
|
||||||
|
* @param index a int.
|
||||||
|
*/
|
||||||
|
fun removeLimits(index: Int) {
|
||||||
|
theState.removeLimits(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* removeLimits.
|
||||||
|
*
|
||||||
|
* @param name a [String] object.
|
||||||
|
*/
|
||||||
|
fun removeLimits(name: String?) {
|
||||||
|
theState.removeLimits(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minuit does a check of the user gradient at the beginning, if this is not
|
||||||
|
* wanted the set this to "false".
|
||||||
|
*
|
||||||
|
* @param check a boolean.
|
||||||
|
*/
|
||||||
|
fun setCheckAnalyticalDerivatives(check: Boolean) {
|
||||||
|
checkAnalyticalDerivatives = check
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setError.
|
||||||
|
*
|
||||||
|
* @param index a int.
|
||||||
|
* @param err a double.
|
||||||
|
*/
|
||||||
|
fun setError(index: Int, err: Double) {
|
||||||
|
theState.setError(index, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setError.
|
||||||
|
*
|
||||||
|
* @param name a [String] object.
|
||||||
|
* @param err a double.
|
||||||
|
*/
|
||||||
|
fun setError(name: String?, err: Double) {
|
||||||
|
theState.setError(name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* errorDef() is the error definition of the function. E.g. is 1 if function
|
||||||
|
* is Chi2 and 0.5 if function is -logLikelihood. If the user wants instead
|
||||||
|
* the 2-sigma errors, errorDef() = 4, as Chi2(x+n*sigma) = Chi2(x) + n*n.
|
||||||
|
*
|
||||||
|
* @param errorDef a double.
|
||||||
|
*/
|
||||||
|
fun setErrorDef(errorDef: Double) {
|
||||||
|
theErrorDef = errorDef
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setLimits.
|
||||||
|
*
|
||||||
|
* @param index a int.
|
||||||
|
* @param low a double.
|
||||||
|
* @param up a double.
|
||||||
|
*/
|
||||||
|
fun setLimits(index: Int, low: Double, up: Double) {
|
||||||
|
theState.setLimits(index, low, up)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setLimits.
|
||||||
|
*
|
||||||
|
* @param name a [String] object.
|
||||||
|
* @param low a double.
|
||||||
|
* @param up a double.
|
||||||
|
*/
|
||||||
|
fun setLimits(name: String?, low: Double, up: Double) {
|
||||||
|
theState.setLimits(name, low, up)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setPrecision.
|
||||||
|
*
|
||||||
|
* @param prec a double.
|
||||||
|
*/
|
||||||
|
fun setPrecision(prec: Double) {
|
||||||
|
theState.setPrecision(prec)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* By default if the function to be minimized implements MultiFunction then
|
||||||
|
* the analytical gradient provided by the function will be used. Set this
|
||||||
|
* to
|
||||||
|
* <CODE>false</CODE> to disable this behaviour and force numerical
|
||||||
|
* calculation of the gradient.
|
||||||
|
*
|
||||||
|
* @param use a boolean.
|
||||||
|
*/
|
||||||
|
fun setUseAnalyticalDerivatives(use: Boolean) {
|
||||||
|
useAnalyticalDerivatives = use
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setValue.
|
||||||
|
*
|
||||||
|
* @param index a int.
|
||||||
|
* @param val a double.
|
||||||
|
*/
|
||||||
|
fun setValue(index: Int, `val`: Double) {
|
||||||
|
theState.setValue(index, `val`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setValue.
|
||||||
|
*
|
||||||
|
* @param name a [String] object.
|
||||||
|
* @param val a double.
|
||||||
|
*/
|
||||||
|
fun setValue(name: String?, `val`: Double) {
|
||||||
|
theState.setValue(name, `val`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* state.
|
||||||
|
*
|
||||||
|
* @return a [hep.dataforge.MINUIT.MnUserParameterState] object.
|
||||||
|
*/
|
||||||
|
fun state(): MnUserParameterState {
|
||||||
|
return theState
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* strategy.
|
||||||
|
*
|
||||||
|
* @return a [hep.dataforge.MINUIT.MnStrategy] object.
|
||||||
|
*/
|
||||||
|
fun strategy(): MnStrategy {
|
||||||
|
return theStrategy
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* useAnalyticalDerivaties.
|
||||||
|
*
|
||||||
|
* @return a boolean.
|
||||||
|
*/
|
||||||
|
fun useAnalyticalDerivaties(): Boolean {
|
||||||
|
return useAnalyticalDerivatives
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* value.
|
||||||
|
*
|
||||||
|
* @param index a int.
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
fun value(index: Int): Double {
|
||||||
|
return theState.value(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* value.
|
||||||
|
*
|
||||||
|
* @param name a [String] object.
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
fun value(name: String?): Double {
|
||||||
|
return theState.value(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* variableParameters.
|
||||||
|
*
|
||||||
|
* @return a int.
|
||||||
|
*/
|
||||||
|
fun variableParameters(): Int {
|
||||||
|
return theState.variableParameters()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
var DEFAULT_MAXFCN = 0
|
||||||
|
var DEFAULT_STRATEGY = 1
|
||||||
|
var DEFAULT_TOLER = 0.1
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,283 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import ru.inr.mass.maths.MultiFunction
|
||||||
|
import ru.inr.mass.minuit.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API class for Contours error analysis (2-dim errors). Minimization has to be
|
||||||
|
* done before and minimum must be valid. Possibility to ask only for the points
|
||||||
|
* or the points and associated Minos errors.
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
* @author Darksnake
|
||||||
|
*/
|
||||||
|
class MnContours(fcn: MultiFunction?, min: FunctionMinimum?, stra: MnStrategy?) {
|
||||||
|
private var theFCN: MultiFunction? = null
|
||||||
|
private var theMinimum: FunctionMinimum? = null
|
||||||
|
private var theStrategy: MnStrategy? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from FCN + minimum
|
||||||
|
*
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
* @param min a [hep.dataforge.MINUIT.FunctionMinimum] object.
|
||||||
|
*/
|
||||||
|
constructor(fcn: MultiFunction?, min: FunctionMinimum?) : this(fcn, min, MnApplication.DEFAULT_STRATEGY)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from FCN + minimum + strategy
|
||||||
|
*
|
||||||
|
* @param stra a int.
|
||||||
|
* @param min a [hep.dataforge.MINUIT.FunctionMinimum] object.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
*/
|
||||||
|
constructor(fcn: MultiFunction?, min: FunctionMinimum?, stra: Int) : this(fcn, min, MnStrategy(stra))
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* contour.
|
||||||
|
*
|
||||||
|
* @param px a int.
|
||||||
|
* @param py a int.
|
||||||
|
* @return a [hep.dataforge.MINUIT.ContoursError] object.
|
||||||
|
*/
|
||||||
|
fun contour(px: Int, py: Int): ContoursError {
|
||||||
|
return contour(px, py, 1.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* contour.
|
||||||
|
*
|
||||||
|
* @param px a int.
|
||||||
|
* @param py a int.
|
||||||
|
* @param errDef a double.
|
||||||
|
* @return a [hep.dataforge.MINUIT.ContoursError] object.
|
||||||
|
*/
|
||||||
|
fun contour(px: Int, py: Int, errDef: Double): ContoursError {
|
||||||
|
return contour(px, py, errDef, 20)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Causes a CONTOURS error analysis and returns the result in form of
|
||||||
|
* ContoursError. As a by-product ContoursError keeps the MinosError
|
||||||
|
* information of parameters parx and pary. The result ContoursError can be
|
||||||
|
* easily printed using MnPrint or toString().
|
||||||
|
*
|
||||||
|
* @param npoints a int.
|
||||||
|
* @param px a int.
|
||||||
|
* @param py a int.
|
||||||
|
* @param errDef a double.
|
||||||
|
* @return a [hep.dataforge.MINUIT.ContoursError] object.
|
||||||
|
*/
|
||||||
|
fun contour(px: Int, py: Int, errDef: Double, npoints: Int): ContoursError {
|
||||||
|
var errDef = errDef
|
||||||
|
errDef *= theMinimum!!.errorDef()
|
||||||
|
assert(npoints > 3)
|
||||||
|
val maxcalls: Int = 100 * (npoints + 5) * (theMinimum!!.userState().variableParameters() + 1)
|
||||||
|
var nfcn = 0
|
||||||
|
val result: MutableList<Range> = java.util.ArrayList<Range>(npoints)
|
||||||
|
val states: List<MnUserParameterState> = java.util.ArrayList<MnUserParameterState>()
|
||||||
|
val toler = 0.05
|
||||||
|
|
||||||
|
//get first four points
|
||||||
|
val minos = MnMinos(theFCN, theMinimum, theStrategy)
|
||||||
|
val valx: Double = theMinimum!!.userState().value(px)
|
||||||
|
val valy: Double = theMinimum!!.userState().value(py)
|
||||||
|
val mex: MinosError = minos.minos(px, errDef)
|
||||||
|
nfcn += mex.nfcn()
|
||||||
|
if (!mex.isValid()) {
|
||||||
|
MINUITPlugin.logStatic("MnContours is unable to find first two points.")
|
||||||
|
return ContoursError(px, py, result, mex, mex, nfcn)
|
||||||
|
}
|
||||||
|
val ex: Range = mex.range()
|
||||||
|
val mey: MinosError = minos.minos(py, errDef)
|
||||||
|
nfcn += mey.nfcn()
|
||||||
|
if (!mey.isValid()) {
|
||||||
|
MINUITPlugin.logStatic("MnContours is unable to find second two points.")
|
||||||
|
return ContoursError(px, py, result, mex, mey, nfcn)
|
||||||
|
}
|
||||||
|
val ey: Range = mey.range()
|
||||||
|
val migrad = MnMigrad(theFCN,
|
||||||
|
theMinimum!!.userState().copy(),
|
||||||
|
MnStrategy(max(0, theStrategy!!.strategy() - 1)))
|
||||||
|
migrad.fix(px)
|
||||||
|
migrad.setValue(px, valx + ex.getSecond())
|
||||||
|
val exy_up: FunctionMinimum = migrad.minimize()
|
||||||
|
nfcn += exy_up.nfcn()
|
||||||
|
if (!exy_up.isValid()) {
|
||||||
|
MINUITPlugin.logStatic("MnContours is unable to find upper y value for x parameter $px.")
|
||||||
|
return ContoursError(px, py, result, mex, mey, nfcn)
|
||||||
|
}
|
||||||
|
migrad.setValue(px, valx + ex.getFirst())
|
||||||
|
val exy_lo: FunctionMinimum = migrad.minimize()
|
||||||
|
nfcn += exy_lo.nfcn()
|
||||||
|
if (!exy_lo.isValid()) {
|
||||||
|
MINUITPlugin.logStatic("MnContours is unable to find lower y value for x parameter $px.")
|
||||||
|
return ContoursError(px, py, result, mex, mey, nfcn)
|
||||||
|
}
|
||||||
|
val migrad1 = MnMigrad(theFCN,
|
||||||
|
theMinimum!!.userState().copy(),
|
||||||
|
MnStrategy(max(0, theStrategy!!.strategy() - 1)))
|
||||||
|
migrad1.fix(py)
|
||||||
|
migrad1.setValue(py, valy + ey.getSecond())
|
||||||
|
val eyx_up: FunctionMinimum = migrad1.minimize()
|
||||||
|
nfcn += eyx_up.nfcn()
|
||||||
|
if (!eyx_up.isValid()) {
|
||||||
|
MINUITPlugin.logStatic("MnContours is unable to find upper x value for y parameter $py.")
|
||||||
|
return ContoursError(px, py, result, mex, mey, nfcn)
|
||||||
|
}
|
||||||
|
migrad1.setValue(py, valy + ey.getFirst())
|
||||||
|
val eyx_lo: FunctionMinimum = migrad1.minimize()
|
||||||
|
nfcn += eyx_lo.nfcn()
|
||||||
|
if (!eyx_lo.isValid()) {
|
||||||
|
MINUITPlugin.logStatic("MnContours is unable to find lower x value for y parameter $py.")
|
||||||
|
return ContoursError(px, py, result, mex, mey, nfcn)
|
||||||
|
}
|
||||||
|
val scalx: Double = 1.0 / (ex.getSecond() - ex.getFirst())
|
||||||
|
val scaly: Double = 1.0 / (ey.getSecond() - ey.getFirst())
|
||||||
|
result.add(Range(valx + ex.getFirst(), exy_lo.userState().value(py)))
|
||||||
|
result.add(Range(eyx_lo.userState().value(px), valy + ey.getFirst()))
|
||||||
|
result.add(Range(valx + ex.getSecond(), exy_up.userState().value(py)))
|
||||||
|
result.add(Range(eyx_up.userState().value(px), valy + ey.getSecond()))
|
||||||
|
val upar: MnUserParameterState = theMinimum!!.userState().copy()
|
||||||
|
upar.fix(px)
|
||||||
|
upar.fix(py)
|
||||||
|
val par = intArrayOf(px, py)
|
||||||
|
val cross = MnFunctionCross(theFCN, upar, theMinimum!!.fval(), theStrategy, errDef)
|
||||||
|
for (i in 4 until npoints) {
|
||||||
|
var idist1: Range = result[result.size - 1]
|
||||||
|
var idist2: Range = result[0]
|
||||||
|
var pos2 = 0
|
||||||
|
val distx: Double = idist1.getFirst() - idist2.getFirst()
|
||||||
|
val disty: Double = idist1.getSecond() - idist2.getSecond()
|
||||||
|
var bigdis = scalx * scalx * distx * distx + scaly * scaly * disty * disty
|
||||||
|
for (j in 0 until result.size - 1) {
|
||||||
|
val ipair: Range = result[j]
|
||||||
|
val distx2: Double = ipair.getFirst() - result[j + 1].getFirst()
|
||||||
|
val disty2: Double = ipair.getSecond() - result[j + 1].getSecond()
|
||||||
|
val dist = scalx * scalx * distx2 * distx2 + scaly * scaly * disty2 * disty2
|
||||||
|
if (dist > bigdis) {
|
||||||
|
bigdis = dist
|
||||||
|
idist1 = ipair
|
||||||
|
idist2 = result[j + 1]
|
||||||
|
pos2 = j + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val a1 = 0.5
|
||||||
|
val a2 = 0.5
|
||||||
|
var sca = 1.0
|
||||||
|
while (true) {
|
||||||
|
if (nfcn > maxcalls) {
|
||||||
|
MINUITPlugin.logStatic("MnContours: maximum number of function calls exhausted.")
|
||||||
|
return ContoursError(px, py, result, mex, mey, nfcn)
|
||||||
|
}
|
||||||
|
val xmidcr: Double = a1 * idist1.getFirst() + a2 * idist2.getFirst()
|
||||||
|
val ymidcr: Double = a1 * idist1.getSecond() + a2 * idist2.getSecond()
|
||||||
|
val xdir: Double = idist2.getSecond() - idist1.getSecond()
|
||||||
|
val ydir: Double = idist1.getFirst() - idist2.getFirst()
|
||||||
|
val scalfac: Double =
|
||||||
|
sca * max(abs(xdir * scalx), abs(ydir * scaly))
|
||||||
|
val xdircr = xdir / scalfac
|
||||||
|
val ydircr = ydir / scalfac
|
||||||
|
val pmid = doubleArrayOf(xmidcr, ymidcr)
|
||||||
|
val pdir = doubleArrayOf(xdircr, ydircr)
|
||||||
|
val opt: MnCross = cross.cross(par, pmid, pdir, toler, maxcalls)
|
||||||
|
nfcn += opt.nfcn()
|
||||||
|
if (opt.isValid()) {
|
||||||
|
val aopt: Double = opt.value()
|
||||||
|
if (pos2 == 0) {
|
||||||
|
result.add(Range(xmidcr + aopt * xdircr, ymidcr + aopt * ydircr))
|
||||||
|
} else {
|
||||||
|
result.add(pos2, Range(xmidcr + aopt * xdircr, ymidcr + aopt * ydircr))
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (sca < 0.0) {
|
||||||
|
MINUITPlugin.logStatic("MnContours is unable to find point " + (i + 1) + " on contour.")
|
||||||
|
MINUITPlugin.logStatic("MnContours finds only $i points.")
|
||||||
|
return ContoursError(px, py, result, mex, mey, nfcn)
|
||||||
|
}
|
||||||
|
sca = -1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ContoursError(px, py, result, mex, mey, nfcn)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* points.
|
||||||
|
*
|
||||||
|
* @param px a int.
|
||||||
|
* @param py a int.
|
||||||
|
* @return a [List] object.
|
||||||
|
*/
|
||||||
|
fun points(px: Int, py: Int): List<Range> {
|
||||||
|
return points(px, py, 1.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* points.
|
||||||
|
*
|
||||||
|
* @param px a int.
|
||||||
|
* @param py a int.
|
||||||
|
* @param errDef a double.
|
||||||
|
* @return a [List] object.
|
||||||
|
*/
|
||||||
|
fun points(px: Int, py: Int, errDef: Double): List<Range> {
|
||||||
|
return points(px, py, errDef, 20)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates one function contour of FCN with respect to parameters parx
|
||||||
|
* and pary. The return value is a list of (x,y) points. FCN minimized
|
||||||
|
* always with respect to all other n - 2 variable parameters (if any).
|
||||||
|
* MINUITPlugin will try to find n points on the contour (default 20). To
|
||||||
|
* calculate more than one contour, the user needs to set the error
|
||||||
|
* definition in its FCN to the appropriate value for the desired confidence
|
||||||
|
* level and call this method for each contour.
|
||||||
|
*
|
||||||
|
* @param npoints a int.
|
||||||
|
* @param px a int.
|
||||||
|
* @param py a int.
|
||||||
|
* @param errDef a double.
|
||||||
|
* @return a [List] object.
|
||||||
|
*/
|
||||||
|
fun points(px: Int, py: Int, errDef: Double, npoints: Int): List<Range> {
|
||||||
|
val cont: ContoursError = contour(px, py, errDef, npoints)
|
||||||
|
return cont.points()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun strategy(): MnStrategy? {
|
||||||
|
return theStrategy
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from FCN + minimum + strategy
|
||||||
|
*
|
||||||
|
* @param stra a [hep.dataforge.MINUIT.MnStrategy] object.
|
||||||
|
* @param min a [hep.dataforge.MINUIT.FunctionMinimum] object.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
*/
|
||||||
|
init {
|
||||||
|
theFCN = fcn
|
||||||
|
theMinimum = min
|
||||||
|
theStrategy = stra
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import space.kscience.kmath.optimization.minuit.MINUITPlugin
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
internal object MnCovarianceSqueeze {
|
||||||
|
fun squeeze(cov: MnUserCovariance, n: Int): MnUserCovariance {
|
||||||
|
assert(cov.nrow() > 0)
|
||||||
|
assert(n < cov.nrow())
|
||||||
|
val hess = MnAlgebraicSymMatrix(cov.nrow())
|
||||||
|
for (i in 0 until cov.nrow()) {
|
||||||
|
for (j in i until cov.nrow()) {
|
||||||
|
hess[i, j] = cov[i, j]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
hess.invert()
|
||||||
|
} catch (x: SingularMatrixException) {
|
||||||
|
MINUITPlugin.logStatic("MnUserCovariance inversion failed; return diagonal matrix;")
|
||||||
|
val result = MnUserCovariance(cov.nrow() - 1)
|
||||||
|
var i = 0
|
||||||
|
var j = 0
|
||||||
|
while (i < cov.nrow()) {
|
||||||
|
if (i == n) {
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result[j, j] = cov[i, i]
|
||||||
|
j++
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
val squeezed: MnAlgebraicSymMatrix = squeeze(hess, n)
|
||||||
|
try {
|
||||||
|
squeezed.invert()
|
||||||
|
} catch (x: SingularMatrixException) {
|
||||||
|
MINUITPlugin.logStatic("MnUserCovariance back-inversion failed; return diagonal matrix;")
|
||||||
|
val result = MnUserCovariance(squeezed.nrow())
|
||||||
|
var i = 0
|
||||||
|
while (i < squeezed.nrow()) {
|
||||||
|
result[i, i] = 1.0 / squeezed[i, i]
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
return MnUserCovariance(squeezed.data(), squeezed.nrow())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun squeeze(err: MinimumError, n: Int): MinimumError {
|
||||||
|
val hess: MnAlgebraicSymMatrix = err.hessian()
|
||||||
|
val squeezed: MnAlgebraicSymMatrix = squeeze(hess, n)
|
||||||
|
try {
|
||||||
|
squeezed.invert()
|
||||||
|
} catch (x: SingularMatrixException) {
|
||||||
|
MINUITPlugin.logStatic("MnCovarianceSqueeze: MinimumError inversion fails; return diagonal matrix.")
|
||||||
|
val tmp = MnAlgebraicSymMatrix(squeezed.nrow())
|
||||||
|
var i = 0
|
||||||
|
while (i < squeezed.nrow()) {
|
||||||
|
tmp[i, i] = 1.0 / squeezed[i, i]
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return MinimumError(tmp, MnInvertFailed())
|
||||||
|
}
|
||||||
|
return MinimumError(squeezed, err.dcovar())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun squeeze(hess: MnAlgebraicSymMatrix, n: Int): MnAlgebraicSymMatrix {
|
||||||
|
assert(hess.nrow() > 0)
|
||||||
|
assert(n < hess.nrow())
|
||||||
|
val hs = MnAlgebraicSymMatrix(hess.nrow() - 1)
|
||||||
|
var i = 0
|
||||||
|
var j = 0
|
||||||
|
while (i < hess.nrow()) {
|
||||||
|
if (i == n) {
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var k = i
|
||||||
|
var l = j
|
||||||
|
while (k < hess.nrow()) {
|
||||||
|
if (k == n) {
|
||||||
|
k++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
hs[j, l] = hess[i, k]
|
||||||
|
l++
|
||||||
|
k++
|
||||||
|
}
|
||||||
|
j++
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return hs
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* MnCross class.
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
* @author Darksnake
|
||||||
|
*/
|
||||||
|
class MnCross {
|
||||||
|
private var theLimset = false
|
||||||
|
private var theMaxFcn = false
|
||||||
|
private var theNFcn = 0
|
||||||
|
private var theNewMin = false
|
||||||
|
private var theState: MnUserParameterState
|
||||||
|
private var theValid = false
|
||||||
|
private var theValue = 0.0
|
||||||
|
|
||||||
|
internal constructor() {
|
||||||
|
theState = MnUserParameterState()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal constructor(nfcn: Int) {
|
||||||
|
theState = MnUserParameterState()
|
||||||
|
theNFcn = nfcn
|
||||||
|
}
|
||||||
|
|
||||||
|
internal constructor(value: Double, state: MnUserParameterState, nfcn: Int) {
|
||||||
|
theValue = value
|
||||||
|
theState = state
|
||||||
|
theNFcn = nfcn
|
||||||
|
theValid = true
|
||||||
|
}
|
||||||
|
|
||||||
|
internal constructor(state: MnUserParameterState, nfcn: Int, x: CrossParLimit?) {
|
||||||
|
theState = state
|
||||||
|
theNFcn = nfcn
|
||||||
|
theLimset = true
|
||||||
|
}
|
||||||
|
|
||||||
|
internal constructor(state: MnUserParameterState, nfcn: Int, x: CrossFcnLimit?) {
|
||||||
|
theState = state
|
||||||
|
theNFcn = nfcn
|
||||||
|
theMaxFcn = true
|
||||||
|
}
|
||||||
|
|
||||||
|
internal constructor(state: MnUserParameterState, nfcn: Int, x: CrossNewMin?) {
|
||||||
|
theState = state
|
||||||
|
theNFcn = nfcn
|
||||||
|
theNewMin = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun atLimit(): Boolean {
|
||||||
|
return theLimset
|
||||||
|
}
|
||||||
|
|
||||||
|
fun atMaxFcn(): Boolean {
|
||||||
|
return theMaxFcn
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isValid(): Boolean {
|
||||||
|
return theValid
|
||||||
|
}
|
||||||
|
|
||||||
|
fun newMinimum(): Boolean {
|
||||||
|
return theNewMin
|
||||||
|
}
|
||||||
|
|
||||||
|
fun nfcn(): Int {
|
||||||
|
return theNFcn
|
||||||
|
}
|
||||||
|
|
||||||
|
fun state(): MnUserParameterState {
|
||||||
|
return theState
|
||||||
|
}
|
||||||
|
|
||||||
|
fun value(): Double {
|
||||||
|
return theValue
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class CrossFcnLimit
|
||||||
|
internal class CrossNewMin
|
||||||
|
internal class CrossParLimit
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import org.apache.commons.math3.linear.RealVector
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates and the eigenvalues of the user covariance matrix
|
||||||
|
* MnUserCovariance.
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
* @author Darksnake
|
||||||
|
*/
|
||||||
|
object MnEigen {
|
||||||
|
/* Calculate eigenvalues of the covariance matrix.
|
||||||
|
* Will perform the calculation of the eigenvalues of the covariance matrix
|
||||||
|
* and return the result in the form of a double array.
|
||||||
|
* The eigenvalues are ordered from the smallest to the largest eigenvalue.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* eigenvalues.
|
||||||
|
*
|
||||||
|
* @param covar a [hep.dataforge.MINUIT.MnUserCovariance] object.
|
||||||
|
* @return an array of double.
|
||||||
|
*/
|
||||||
|
fun eigenvalues(covar: MnUserCovariance): DoubleArray {
|
||||||
|
val cov = MnAlgebraicSymMatrix(covar.nrow())
|
||||||
|
for (i in 0 until covar.nrow()) {
|
||||||
|
for (j in i until covar.nrow()) {
|
||||||
|
cov[i, j] = covar[i, j]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val eigen: RealVector = cov.eigenvalues()
|
||||||
|
return eigen.toArray()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import ru.inr.mass.maths.MultiFunction
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Функция, которая помнит количество вызовов себя и ErrorDef
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
class MnFcn(fcn: MultiFunction?, errorDef: Double) {
|
||||||
|
private val theErrorDef: Double
|
||||||
|
private val theFCN: MultiFunction?
|
||||||
|
protected var theNumCall: Int
|
||||||
|
fun errorDef(): Double {
|
||||||
|
return theErrorDef
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fcn(): MultiFunction? {
|
||||||
|
return theFCN
|
||||||
|
}
|
||||||
|
|
||||||
|
fun numOfCalls(): Int {
|
||||||
|
return theNumCall
|
||||||
|
}
|
||||||
|
|
||||||
|
fun value(v: RealVector): Double {
|
||||||
|
theNumCall++
|
||||||
|
return theFCN.value(v.toArray())
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
theFCN = fcn
|
||||||
|
theNumCall = 0
|
||||||
|
theErrorDef = errorDef
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,369 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import ru.inr.mass.maths.MultiFunction
|
||||||
|
import ru.inr.mass.minuit.*
|
||||||
|
import kotlin.math.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
internal class MnFunctionCross(
|
||||||
|
fcn: MultiFunction?,
|
||||||
|
state: MnUserParameterState,
|
||||||
|
fval: Double,
|
||||||
|
stra: MnStrategy?,
|
||||||
|
errorDef: Double
|
||||||
|
) {
|
||||||
|
private val theErrorDef: Double
|
||||||
|
private val theFCN: MultiFunction?
|
||||||
|
private val theFval: Double
|
||||||
|
private val theState: MnUserParameterState
|
||||||
|
private val theStrategy: MnStrategy?
|
||||||
|
fun cross(par: IntArray, pmid: DoubleArray, pdir: DoubleArray, tlr: Double, maxcalls: Int): MnCross {
|
||||||
|
val npar = par.size
|
||||||
|
var nfcn = 0
|
||||||
|
val prec: MnMachinePrecision = theState.precision()
|
||||||
|
val tlf = tlr * theErrorDef
|
||||||
|
var tla = tlr
|
||||||
|
val maxitr = 15
|
||||||
|
var ipt = 0
|
||||||
|
val aminsv = theFval
|
||||||
|
val aim = aminsv + theErrorDef
|
||||||
|
var aopt = 0.0
|
||||||
|
var limset = false
|
||||||
|
val alsb = DoubleArray(3)
|
||||||
|
val flsb = DoubleArray(3)
|
||||||
|
val up = theErrorDef
|
||||||
|
var aulim = 100.0
|
||||||
|
for (i in par.indices) {
|
||||||
|
val kex = par[i]
|
||||||
|
if (theState.parameter(kex).hasLimits()) {
|
||||||
|
val zmid = pmid[i]
|
||||||
|
val zdir = pdir[i]
|
||||||
|
if (abs(zdir) < theState.precision().eps()) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (zdir > 0.0 && theState.parameter(kex).hasUpperLimit()) {
|
||||||
|
val zlim: Double = theState.parameter(kex).upperLimit()
|
||||||
|
aulim = min(aulim, (zlim - zmid) / zdir)
|
||||||
|
} else if (zdir < 0.0 && theState.parameter(kex).hasLowerLimit()) {
|
||||||
|
val zlim: Double = theState.parameter(kex).lowerLimit()
|
||||||
|
aulim = min(aulim, (zlim - zmid) / zdir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (aulim < aopt + tla) {
|
||||||
|
limset = true
|
||||||
|
}
|
||||||
|
val migrad = MnMigrad(theFCN, theState, MnStrategy(max(0, theStrategy!!.strategy() - 1)))
|
||||||
|
for (i in 0 until npar) {
|
||||||
|
migrad.setValue(par[i], pmid[i])
|
||||||
|
}
|
||||||
|
val min0: FunctionMinimum = migrad.minimize(maxcalls, tlr)
|
||||||
|
nfcn += min0.nfcn()
|
||||||
|
if (min0.hasReachedCallLimit()) {
|
||||||
|
return MnCross(min0.userState(), nfcn, MnCross.CrossFcnLimit())
|
||||||
|
}
|
||||||
|
if (!min0.isValid()) {
|
||||||
|
return MnCross(nfcn)
|
||||||
|
}
|
||||||
|
if (limset && min0.fval() < aim) {
|
||||||
|
return MnCross(min0.userState(), nfcn, MnCross.CrossParLimit())
|
||||||
|
}
|
||||||
|
ipt++
|
||||||
|
alsb[0] = 0.0
|
||||||
|
flsb[0] = min0.fval()
|
||||||
|
flsb[0] = max(flsb[0], aminsv + 0.1 * up)
|
||||||
|
aopt = sqrt(up / (flsb[0] - aminsv)) - 1.0
|
||||||
|
if (abs(flsb[0] - aim) < tlf) {
|
||||||
|
return MnCross(aopt, min0.userState(), nfcn)
|
||||||
|
}
|
||||||
|
if (aopt > 1.0) {
|
||||||
|
aopt = 1.0
|
||||||
|
}
|
||||||
|
if (aopt < -0.5) {
|
||||||
|
aopt = -0.5
|
||||||
|
}
|
||||||
|
limset = false
|
||||||
|
if (aopt > aulim) {
|
||||||
|
aopt = aulim
|
||||||
|
limset = true
|
||||||
|
}
|
||||||
|
for (i in 0 until npar) {
|
||||||
|
migrad.setValue(par[i], pmid[i] + aopt * pdir[i])
|
||||||
|
}
|
||||||
|
var min1: FunctionMinimum = migrad.minimize(maxcalls, tlr)
|
||||||
|
nfcn += min1.nfcn()
|
||||||
|
if (min1.hasReachedCallLimit()) {
|
||||||
|
return MnCross(min1.userState(), nfcn, MnCross.CrossFcnLimit())
|
||||||
|
}
|
||||||
|
if (!min1.isValid()) {
|
||||||
|
return MnCross(nfcn)
|
||||||
|
}
|
||||||
|
if (limset && min1.fval() < aim) {
|
||||||
|
return MnCross(min1.userState(), nfcn, MnCross.CrossParLimit())
|
||||||
|
}
|
||||||
|
ipt++
|
||||||
|
alsb[1] = aopt
|
||||||
|
flsb[1] = min1.fval()
|
||||||
|
var dfda = (flsb[1] - flsb[0]) / (alsb[1] - alsb[0])
|
||||||
|
var ecarmn = 0.0
|
||||||
|
var ecarmx = 0.0
|
||||||
|
var ibest = 0
|
||||||
|
var iworst = 0
|
||||||
|
var noless = 0
|
||||||
|
var min2: FunctionMinimum? = null
|
||||||
|
L300@ while (true) {
|
||||||
|
if (dfda < 0.0) {
|
||||||
|
val maxlk = maxitr - ipt
|
||||||
|
for (it in 0 until maxlk) {
|
||||||
|
alsb[0] = alsb[1]
|
||||||
|
flsb[0] = flsb[1]
|
||||||
|
aopt = alsb[0] + 0.2 * it
|
||||||
|
limset = false
|
||||||
|
if (aopt > aulim) {
|
||||||
|
aopt = aulim
|
||||||
|
limset = true
|
||||||
|
}
|
||||||
|
for (i in 0 until npar) {
|
||||||
|
migrad.setValue(par[i], pmid[i] + aopt * pdir[i])
|
||||||
|
}
|
||||||
|
min1 = migrad.minimize(maxcalls, tlr)
|
||||||
|
nfcn += min1.nfcn()
|
||||||
|
if (min1.hasReachedCallLimit()) {
|
||||||
|
return MnCross(min1.userState(), nfcn, MnCross.CrossFcnLimit())
|
||||||
|
}
|
||||||
|
if (!min1.isValid()) {
|
||||||
|
return MnCross(nfcn)
|
||||||
|
}
|
||||||
|
if (limset && min1.fval() < aim) {
|
||||||
|
return MnCross(min1.userState(), nfcn, MnCross.CrossParLimit())
|
||||||
|
}
|
||||||
|
ipt++
|
||||||
|
alsb[1] = aopt
|
||||||
|
flsb[1] = min1.fval()
|
||||||
|
dfda = (flsb[1] - flsb[0]) / (alsb[1] - alsb[0])
|
||||||
|
if (dfda > 0.0) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ipt > maxitr) {
|
||||||
|
return MnCross(nfcn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
L460@ while (true) {
|
||||||
|
aopt = alsb[1] + (aim - flsb[1]) / dfda
|
||||||
|
val fdist: Double =
|
||||||
|
min(abs(aim - flsb[0]), abs(aim - flsb[1]))
|
||||||
|
val adist: Double =
|
||||||
|
min(abs(aopt - alsb[0]), abs(aopt - alsb[1]))
|
||||||
|
tla = tlr
|
||||||
|
if (abs(aopt) > 1.0) {
|
||||||
|
tla = tlr * abs(aopt)
|
||||||
|
}
|
||||||
|
if (adist < tla && fdist < tlf) {
|
||||||
|
return MnCross(aopt, min1.userState(), nfcn)
|
||||||
|
}
|
||||||
|
if (ipt > maxitr) {
|
||||||
|
return MnCross(nfcn)
|
||||||
|
}
|
||||||
|
val bmin: Double = min(alsb[0], alsb[1]) - 1.0
|
||||||
|
if (aopt < bmin) {
|
||||||
|
aopt = bmin
|
||||||
|
}
|
||||||
|
val bmax: Double = max(alsb[0], alsb[1]) + 1.0
|
||||||
|
if (aopt > bmax) {
|
||||||
|
aopt = bmax
|
||||||
|
}
|
||||||
|
limset = false
|
||||||
|
if (aopt > aulim) {
|
||||||
|
aopt = aulim
|
||||||
|
limset = true
|
||||||
|
}
|
||||||
|
for (i in 0 until npar) {
|
||||||
|
migrad.setValue(par[i], pmid[i] + aopt * pdir[i])
|
||||||
|
}
|
||||||
|
min2 = migrad.minimize(maxcalls, tlr)
|
||||||
|
nfcn += min2.nfcn()
|
||||||
|
if (min2.hasReachedCallLimit()) {
|
||||||
|
return MnCross(min2.userState(), nfcn, CrossFcnLimit())
|
||||||
|
}
|
||||||
|
if (!min2.isValid()) {
|
||||||
|
return MnCross(nfcn)
|
||||||
|
}
|
||||||
|
if (limset && min2.fval() < aim) {
|
||||||
|
return MnCross(min2.userState(), nfcn, MnCross.CrossParLimit())
|
||||||
|
}
|
||||||
|
ipt++
|
||||||
|
alsb[2] = aopt
|
||||||
|
flsb[2] = min2.fval()
|
||||||
|
ecarmn = abs(flsb[2] - aim)
|
||||||
|
ecarmx = 0.0
|
||||||
|
ibest = 2
|
||||||
|
iworst = 0
|
||||||
|
noless = 0
|
||||||
|
for (i in 0..2) {
|
||||||
|
val ecart: Double = abs(flsb[i] - aim)
|
||||||
|
if (ecart > ecarmx) {
|
||||||
|
ecarmx = ecart
|
||||||
|
iworst = i
|
||||||
|
}
|
||||||
|
if (ecart < ecarmn) {
|
||||||
|
ecarmn = ecart
|
||||||
|
ibest = i
|
||||||
|
}
|
||||||
|
if (flsb[i] < aim) {
|
||||||
|
noless++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (noless == 1 || noless == 2) {
|
||||||
|
break@L300
|
||||||
|
}
|
||||||
|
if (noless == 0 && ibest != 2) {
|
||||||
|
return MnCross(nfcn)
|
||||||
|
}
|
||||||
|
if (noless == 3 && ibest != 2) {
|
||||||
|
alsb[1] = alsb[2]
|
||||||
|
flsb[1] = flsb[2]
|
||||||
|
continue@L300
|
||||||
|
}
|
||||||
|
flsb[iworst] = flsb[2]
|
||||||
|
alsb[iworst] = alsb[2]
|
||||||
|
dfda = (flsb[1] - flsb[0]) / (alsb[1] - alsb[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
val parbol: MnParabola = MnParabolaFactory.create(MnParabolaPoint(alsb[0], flsb[0]),
|
||||||
|
MnParabolaPoint(alsb[1], flsb[1]),
|
||||||
|
MnParabolaPoint(
|
||||||
|
alsb[2], flsb[2]))
|
||||||
|
val coeff1: Double = parbol.c()
|
||||||
|
val coeff2: Double = parbol.b()
|
||||||
|
val coeff3: Double = parbol.a()
|
||||||
|
val determ = coeff2 * coeff2 - 4.0 * coeff3 * (coeff1 - aim)
|
||||||
|
if (determ < prec.eps()) {
|
||||||
|
return MnCross(nfcn)
|
||||||
|
}
|
||||||
|
val rt: Double = sqrt(determ)
|
||||||
|
val x1 = (-coeff2 + rt) / (2.0 * coeff3)
|
||||||
|
val x2 = (-coeff2 - rt) / (2.0 * coeff3)
|
||||||
|
val s1 = coeff2 + 2.0 * x1 * coeff3
|
||||||
|
val s2 = coeff2 + 2.0 * x2 * coeff3
|
||||||
|
if (s1 * s2 > 0.0) {
|
||||||
|
MINUITPlugin.logStatic("MnFunctionCross problem 1")
|
||||||
|
}
|
||||||
|
aopt = x1
|
||||||
|
var slope = s1
|
||||||
|
if (s2 > 0.0) {
|
||||||
|
aopt = x2
|
||||||
|
slope = s2
|
||||||
|
}
|
||||||
|
tla = tlr
|
||||||
|
if (abs(aopt) > 1.0) {
|
||||||
|
tla = tlr * abs(aopt)
|
||||||
|
}
|
||||||
|
if (abs(aopt - alsb[ibest]) < tla && abs(flsb[ibest] - aim) < tlf) {
|
||||||
|
return MnCross(aopt, min2!!.userState(), nfcn)
|
||||||
|
}
|
||||||
|
var ileft = 3
|
||||||
|
var iright = 3
|
||||||
|
var iout = 3
|
||||||
|
ibest = 0
|
||||||
|
ecarmx = 0.0
|
||||||
|
ecarmn = abs(aim - flsb[0])
|
||||||
|
for (i in 0..2) {
|
||||||
|
val ecart: Double = abs(flsb[i] - aim)
|
||||||
|
if (ecart < ecarmn) {
|
||||||
|
ecarmn = ecart
|
||||||
|
ibest = i
|
||||||
|
}
|
||||||
|
if (ecart > ecarmx) {
|
||||||
|
ecarmx = ecart
|
||||||
|
}
|
||||||
|
if (flsb[i] > aim) {
|
||||||
|
if (iright == 3) {
|
||||||
|
iright = i
|
||||||
|
} else if (flsb[i] > flsb[iright]) {
|
||||||
|
iout = i
|
||||||
|
} else {
|
||||||
|
iout = iright
|
||||||
|
iright = i
|
||||||
|
}
|
||||||
|
} else if (ileft == 3) {
|
||||||
|
ileft = i
|
||||||
|
} else if (flsb[i] < flsb[ileft]) {
|
||||||
|
iout = i
|
||||||
|
} else {
|
||||||
|
iout = ileft
|
||||||
|
ileft = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ecarmx > 10.0 * abs(flsb[iout] - aim)) {
|
||||||
|
aopt = 0.5 * (aopt + 0.5 * (alsb[iright] + alsb[ileft]))
|
||||||
|
}
|
||||||
|
var smalla = 0.1 * tla
|
||||||
|
if (slope * smalla > tlf) {
|
||||||
|
smalla = tlf / slope
|
||||||
|
}
|
||||||
|
val aleft = alsb[ileft] + smalla
|
||||||
|
val aright = alsb[iright] - smalla
|
||||||
|
if (aopt < aleft) {
|
||||||
|
aopt = aleft
|
||||||
|
}
|
||||||
|
if (aopt > aright) {
|
||||||
|
aopt = aright
|
||||||
|
}
|
||||||
|
if (aleft > aright) {
|
||||||
|
aopt = 0.5 * (aleft + aright)
|
||||||
|
}
|
||||||
|
limset = false
|
||||||
|
if (aopt > aulim) {
|
||||||
|
aopt = aulim
|
||||||
|
limset = true
|
||||||
|
}
|
||||||
|
for (i in 0 until npar) {
|
||||||
|
migrad.setValue(par[i], pmid[i] + aopt * pdir[i])
|
||||||
|
}
|
||||||
|
min2 = migrad.minimize(maxcalls, tlr)
|
||||||
|
nfcn += min2.nfcn()
|
||||||
|
if (min2.hasReachedCallLimit()) {
|
||||||
|
return MnCross(min2.userState(), nfcn, CrossFcnLimit())
|
||||||
|
}
|
||||||
|
if (!min2.isValid()) {
|
||||||
|
return MnCross(nfcn)
|
||||||
|
}
|
||||||
|
if (limset && min2.fval() < aim) {
|
||||||
|
return MnCross(min2.userState(), nfcn, CrossParLimit())
|
||||||
|
}
|
||||||
|
ipt++
|
||||||
|
alsb[iout] = aopt
|
||||||
|
flsb[iout] = min2.fval()
|
||||||
|
ibest = iout
|
||||||
|
} while (ipt < maxitr)
|
||||||
|
return MnCross(nfcn)
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
theFCN = fcn
|
||||||
|
theState = state
|
||||||
|
theFval = fval
|
||||||
|
theStrategy = stra
|
||||||
|
theErrorDef = errorDef
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import org.apache.commons.math3.linear.SingularMatrixException
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* MnGlobalCorrelationCoeff class.
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
* @author Darksnake
|
||||||
|
*/
|
||||||
|
class MnGlobalCorrelationCoeff {
|
||||||
|
private var theGlobalCC: DoubleArray
|
||||||
|
private var theValid = false
|
||||||
|
|
||||||
|
internal constructor() {
|
||||||
|
theGlobalCC = DoubleArray(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal constructor(cov: MnAlgebraicSymMatrix) {
|
||||||
|
try {
|
||||||
|
val inv: MnAlgebraicSymMatrix = cov.copy()
|
||||||
|
inv.invert()
|
||||||
|
theGlobalCC = DoubleArray(cov.nrow())
|
||||||
|
for (i in 0 until cov.nrow()) {
|
||||||
|
val denom: Double = inv[i, i] * cov[i, i]
|
||||||
|
if (denom < 1.0 && denom > 0.0) {
|
||||||
|
theGlobalCC[i] = 0
|
||||||
|
} else {
|
||||||
|
theGlobalCC[i] = sqrt(1.0 - 1.0 / denom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
theValid = true
|
||||||
|
} catch (x: SingularMatrixException) {
|
||||||
|
theValid = false
|
||||||
|
theGlobalCC = DoubleArray(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* globalCC.
|
||||||
|
*
|
||||||
|
* @return an array of double.
|
||||||
|
*/
|
||||||
|
fun globalCC(): DoubleArray {
|
||||||
|
return theGlobalCC
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* isValid.
|
||||||
|
*
|
||||||
|
* @return a boolean.
|
||||||
|
*/
|
||||||
|
fun isValid(): Boolean {
|
||||||
|
return theValid
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
override fun toString(): String {
|
||||||
|
return MnPrint.toString(this)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,371 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import ru.inr.mass.maths.MultiFunction
|
||||||
|
import ru.inr.mass.minuit.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* With MnHesse the user can instructs MINUITPlugin to calculate, by finite
|
||||||
|
* differences, the Hessian or error matrix. That is, it calculates the full
|
||||||
|
* matrix of second derivatives of the function with respect to the currently
|
||||||
|
* variable parameters, and inverts it.
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
* @author Darksnake
|
||||||
|
*/
|
||||||
|
class MnHesse {
|
||||||
|
private var theStrategy: MnStrategy
|
||||||
|
|
||||||
|
/**
|
||||||
|
* default constructor with default strategy
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
theStrategy = MnStrategy(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* constructor with user-defined strategy level
|
||||||
|
*
|
||||||
|
* @param stra a int.
|
||||||
|
*/
|
||||||
|
constructor(stra: Int) {
|
||||||
|
theStrategy = MnStrategy(stra)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* conctructor with specific strategy
|
||||||
|
*
|
||||||
|
* @param stra a [hep.dataforge.MINUIT.MnStrategy] object.
|
||||||
|
*/
|
||||||
|
constructor(stra: MnStrategy) {
|
||||||
|
theStrategy = stra
|
||||||
|
}
|
||||||
|
///
|
||||||
|
/// low-level API
|
||||||
|
///
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* calculate.
|
||||||
|
*
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
* @param par an array of double.
|
||||||
|
* @param err an array of double.
|
||||||
|
* @return a [hep.dataforge.MINUIT.MnUserParameterState] object.
|
||||||
|
*/
|
||||||
|
fun calculate(fcn: MultiFunction?, par: DoubleArray, err: DoubleArray): MnUserParameterState {
|
||||||
|
return calculate(fcn, par, err, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FCN + parameters + errors
|
||||||
|
*
|
||||||
|
* @param maxcalls a int.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
* @param par an array of double.
|
||||||
|
* @param err an array of double.
|
||||||
|
* @return a [hep.dataforge.MINUIT.MnUserParameterState] object.
|
||||||
|
*/
|
||||||
|
fun calculate(fcn: MultiFunction?, par: DoubleArray, err: DoubleArray, maxcalls: Int): MnUserParameterState {
|
||||||
|
return calculate(fcn, MnUserParameterState(par, err), maxcalls)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* calculate.
|
||||||
|
*
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
* @param par an array of double.
|
||||||
|
* @param cov a [hep.dataforge.MINUIT.MnUserCovariance] object.
|
||||||
|
* @return a [hep.dataforge.MINUIT.MnUserParameterState] object.
|
||||||
|
*/
|
||||||
|
fun calculate(fcn: MultiFunction?, par: DoubleArray, cov: MnUserCovariance): MnUserParameterState {
|
||||||
|
return calculate(fcn, par, cov, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FCN + parameters + MnUserCovariance
|
||||||
|
*
|
||||||
|
* @param maxcalls a int.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
* @param par an array of double.
|
||||||
|
* @param cov a [hep.dataforge.MINUIT.MnUserCovariance] object.
|
||||||
|
* @return a [hep.dataforge.MINUIT.MnUserParameterState] object.
|
||||||
|
*/
|
||||||
|
fun calculate(fcn: MultiFunction?, par: DoubleArray, cov: MnUserCovariance, maxcalls: Int): MnUserParameterState {
|
||||||
|
return calculate(fcn, MnUserParameterState(par, cov), maxcalls)
|
||||||
|
}
|
||||||
|
///
|
||||||
|
/// high-level API
|
||||||
|
///
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* calculate.
|
||||||
|
*
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
* @param par a [hep.dataforge.MINUIT.MnUserParameters] object.
|
||||||
|
* @return a [hep.dataforge.MINUIT.MnUserParameterState] object.
|
||||||
|
*/
|
||||||
|
fun calculate(fcn: MultiFunction?, par: MnUserParameters): MnUserParameterState {
|
||||||
|
return calculate(fcn, par, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FCN + MnUserParameters
|
||||||
|
*
|
||||||
|
* @param maxcalls a int.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
* @param par a [hep.dataforge.MINUIT.MnUserParameters] object.
|
||||||
|
* @return a [hep.dataforge.MINUIT.MnUserParameterState] object.
|
||||||
|
*/
|
||||||
|
fun calculate(fcn: MultiFunction?, par: MnUserParameters, maxcalls: Int): MnUserParameterState {
|
||||||
|
return calculate(fcn, MnUserParameterState(par), maxcalls)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* calculate.
|
||||||
|
*
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
* @param par a [hep.dataforge.MINUIT.MnUserParameters] object.
|
||||||
|
* @param cov a [hep.dataforge.MINUIT.MnUserCovariance] object.
|
||||||
|
* @return a [hep.dataforge.MINUIT.MnUserParameterState] object.
|
||||||
|
*/
|
||||||
|
fun calculate(fcn: MultiFunction?, par: MnUserParameters, cov: MnUserCovariance?): MnUserParameterState {
|
||||||
|
return calculate(fcn, par, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FCN + MnUserParameters + MnUserCovariance
|
||||||
|
*
|
||||||
|
* @param maxcalls a int.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
* @param par a [hep.dataforge.MINUIT.MnUserParameters] object.
|
||||||
|
* @param cov a [hep.dataforge.MINUIT.MnUserCovariance] object.
|
||||||
|
* @return a [hep.dataforge.MINUIT.MnUserParameterState] object.
|
||||||
|
*/
|
||||||
|
fun calculate(
|
||||||
|
fcn: MultiFunction?,
|
||||||
|
par: MnUserParameters,
|
||||||
|
cov: MnUserCovariance,
|
||||||
|
maxcalls: Int
|
||||||
|
): MnUserParameterState {
|
||||||
|
return calculate(fcn, MnUserParameterState(par, cov), maxcalls)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FCN + MnUserParameterState
|
||||||
|
*
|
||||||
|
* @param maxcalls a int.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
* @param state a [hep.dataforge.MINUIT.MnUserParameterState] object.
|
||||||
|
* @return a [hep.dataforge.MINUIT.MnUserParameterState] object.
|
||||||
|
*/
|
||||||
|
fun calculate(fcn: MultiFunction?, state: MnUserParameterState, maxcalls: Int): MnUserParameterState {
|
||||||
|
val errDef = 1.0 // FixMe!
|
||||||
|
val n: Int = state.variableParameters()
|
||||||
|
val mfcn = MnUserFcn(fcn, errDef, state.getTransformation())
|
||||||
|
val x: RealVector = ArrayRealVector(n)
|
||||||
|
for (i in 0 until n) {
|
||||||
|
x.setEntry(i, state.intParameters()[i])
|
||||||
|
}
|
||||||
|
val amin: Double = mfcn.value(x)
|
||||||
|
val gc = Numerical2PGradientCalculator(mfcn, state.getTransformation(), theStrategy)
|
||||||
|
val par = MinimumParameters(x, amin)
|
||||||
|
val gra: FunctionGradient = gc.gradient(par)
|
||||||
|
val tmp: MinimumState = calculate(mfcn,
|
||||||
|
MinimumState(par, MinimumError(MnAlgebraicSymMatrix(n), 1.0), gra, state.edm(), state.nfcn()),
|
||||||
|
state.getTransformation(),
|
||||||
|
maxcalls)
|
||||||
|
return MnUserParameterState(tmp, errDef, state.getTransformation())
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// internal interface
|
||||||
|
///
|
||||||
|
fun calculate(mfcn: MnFcn, st: MinimumState, trafo: MnUserTransformation, maxcalls: Int): MinimumState {
|
||||||
|
var maxcalls = maxcalls
|
||||||
|
val prec: MnMachinePrecision = trafo.precision()
|
||||||
|
// make sure starting at the right place
|
||||||
|
val amin: Double = mfcn.value(st.vec())
|
||||||
|
val aimsag: Double = sqrt(prec.eps2()) * (abs(amin) + mfcn.errorDef())
|
||||||
|
|
||||||
|
// diagonal elements first
|
||||||
|
val n: Int = st.parameters().vec().getDimension()
|
||||||
|
if (maxcalls == 0) {
|
||||||
|
maxcalls = 200 + 100 * n + 5 * n * n
|
||||||
|
}
|
||||||
|
var vhmat = MnAlgebraicSymMatrix(n)
|
||||||
|
var g2: RealVector = st.gradient().getGradientDerivative().copy()
|
||||||
|
var gst: RealVector = st.gradient().getStep().copy()
|
||||||
|
var grd: RealVector = st.gradient().getGradient().copy()
|
||||||
|
var dirin: RealVector = st.gradient().getStep().copy()
|
||||||
|
val yy: RealVector = ArrayRealVector(n)
|
||||||
|
if (st.gradient().isAnalytical()) {
|
||||||
|
val igc = InitialGradientCalculator(mfcn, trafo, theStrategy)
|
||||||
|
val tmp: FunctionGradient = igc.gradient(st.parameters())
|
||||||
|
gst = tmp.getStep().copy()
|
||||||
|
dirin = tmp.getStep().copy()
|
||||||
|
g2 = tmp.getGradientDerivative().copy()
|
||||||
|
}
|
||||||
|
return try {
|
||||||
|
val x: RealVector = st.parameters().vec().copy()
|
||||||
|
for (i in 0 until n) {
|
||||||
|
val xtf: Double = x.getEntry(i)
|
||||||
|
val dmin: Double = 8.0 * prec.eps2() * (abs(xtf) + prec.eps2())
|
||||||
|
var d: Double = abs(gst.getEntry(i))
|
||||||
|
if (d < dmin) {
|
||||||
|
d = dmin
|
||||||
|
}
|
||||||
|
for (icyc in 0 until ncycles()) {
|
||||||
|
var sag = 0.0
|
||||||
|
var fs1 = 0.0
|
||||||
|
var fs2 = 0.0
|
||||||
|
var multpy = 0
|
||||||
|
while (multpy < 5) {
|
||||||
|
x.setEntry(i, xtf + d)
|
||||||
|
fs1 = mfcn.value(x)
|
||||||
|
x.setEntry(i, xtf - d)
|
||||||
|
fs2 = mfcn.value(x)
|
||||||
|
x.setEntry(i, xtf)
|
||||||
|
sag = 0.5 * (fs1 + fs2 - 2.0 * amin)
|
||||||
|
if (sag > prec.eps2()) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (trafo.parameter(i).hasLimits()) {
|
||||||
|
if (d > 0.5) {
|
||||||
|
throw MnHesseFailedException("MnHesse: 2nd derivative zero for parameter")
|
||||||
|
}
|
||||||
|
d *= 10.0
|
||||||
|
if (d > 0.5) {
|
||||||
|
d = 0.51
|
||||||
|
}
|
||||||
|
multpy++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
d *= 10.0
|
||||||
|
multpy++
|
||||||
|
}
|
||||||
|
if (multpy >= 5) {
|
||||||
|
throw MnHesseFailedException("MnHesse: 2nd derivative zero for parameter")
|
||||||
|
}
|
||||||
|
val g2bfor: Double = g2.getEntry(i)
|
||||||
|
g2.setEntry(i, 2.0 * sag / (d * d))
|
||||||
|
grd.setEntry(i, (fs1 - fs2) / (2.0 * d))
|
||||||
|
gst.setEntry(i, d)
|
||||||
|
dirin.setEntry(i, d)
|
||||||
|
yy.setEntry(i, fs1)
|
||||||
|
val dlast = d
|
||||||
|
d = sqrt(2.0 * aimsag / abs(g2.getEntry(i)))
|
||||||
|
if (trafo.parameter(i).hasLimits()) {
|
||||||
|
d = min(0.5, d)
|
||||||
|
}
|
||||||
|
if (d < dmin) {
|
||||||
|
d = dmin
|
||||||
|
}
|
||||||
|
|
||||||
|
// see if converged
|
||||||
|
if (abs((d - dlast) / d) < tolerstp()) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (abs((g2.getEntry(i) - g2bfor) / g2.getEntry(i)) < tolerg2()) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
d = min(d, 10.0 * dlast)
|
||||||
|
d = max(d, 0.1 * dlast)
|
||||||
|
}
|
||||||
|
vhmat[i, i] = g2.getEntry(i)
|
||||||
|
if (mfcn.numOfCalls() - st.nfcn() > maxcalls) {
|
||||||
|
throw MnHesseFailedException("MnHesse: maximum number of allowed function calls exhausted.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (theStrategy.strategy() > 0) {
|
||||||
|
// refine first derivative
|
||||||
|
val hgc = HessianGradientCalculator(mfcn, trafo, theStrategy)
|
||||||
|
val gr: FunctionGradient = hgc.gradient(st.parameters(), FunctionGradient(grd, g2, gst))
|
||||||
|
grd = gr.getGradient()
|
||||||
|
}
|
||||||
|
|
||||||
|
//off-diagonal elements
|
||||||
|
for (i in 0 until n) {
|
||||||
|
x.setEntry(i, x.getEntry(i) + dirin.getEntry(i))
|
||||||
|
for (j in i + 1 until n) {
|
||||||
|
x.setEntry(j, x.getEntry(j) + dirin.getEntry(j))
|
||||||
|
val fs1: Double = mfcn.value(x)
|
||||||
|
val elem: Double =
|
||||||
|
(fs1 + amin - yy.getEntry(i) - yy.getEntry(j)) / (dirin.getEntry(i) * dirin.getEntry(j))
|
||||||
|
vhmat[i, j] = elem
|
||||||
|
x.setEntry(j, x.getEntry(j) - dirin.getEntry(j))
|
||||||
|
}
|
||||||
|
x.setEntry(i, x.getEntry(i) - dirin.getEntry(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
//verify if matrix pos-def (still 2nd derivative)
|
||||||
|
val tmp: MinimumError = MnPosDef.test(MinimumError(vhmat, 1.0), prec)
|
||||||
|
vhmat = tmp.invHessian()
|
||||||
|
try {
|
||||||
|
vhmat.invert()
|
||||||
|
} catch (xx: SingularMatrixException) {
|
||||||
|
throw MnHesseFailedException("MnHesse: matrix inversion fails!")
|
||||||
|
}
|
||||||
|
val gr = FunctionGradient(grd, g2, gst)
|
||||||
|
if (tmp.isMadePosDef()) {
|
||||||
|
MINUITPlugin.logStatic("MnHesse: matrix is invalid!")
|
||||||
|
MINUITPlugin.logStatic("MnHesse: matrix is not pos. def.!")
|
||||||
|
MINUITPlugin.logStatic("MnHesse: matrix was forced pos. def.")
|
||||||
|
return MinimumState(st.parameters(),
|
||||||
|
MinimumError(vhmat, MnMadePosDef()),
|
||||||
|
gr,
|
||||||
|
st.edm(),
|
||||||
|
mfcn.numOfCalls())
|
||||||
|
}
|
||||||
|
|
||||||
|
//calculate edm
|
||||||
|
val err = MinimumError(vhmat, 0.0)
|
||||||
|
val edm: Double = VariableMetricEDMEstimator().estimate(gr, err)
|
||||||
|
MinimumState(st.parameters(), err, gr, edm, mfcn.numOfCalls())
|
||||||
|
} catch (x: MnHesseFailedException) {
|
||||||
|
MINUITPlugin.logStatic(x.message)
|
||||||
|
MINUITPlugin.logStatic("MnHesse fails and will return diagonal matrix ")
|
||||||
|
var j = 0
|
||||||
|
while (j < n) {
|
||||||
|
val tmp = if (g2.getEntry(j) < prec.eps2()) 1.0 else 1.0 / g2.getEntry(j)
|
||||||
|
vhmat[j, j] = if (tmp < prec.eps2()) 1.0 else tmp
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
MinimumState(st.parameters(),
|
||||||
|
MinimumError(vhmat, MnHesseFailed()),
|
||||||
|
st.gradient(),
|
||||||
|
st.edm(),
|
||||||
|
st.nfcn() + mfcn.numOfCalls())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// forward interface of MnStrategy
|
||||||
|
fun ncycles(): Int {
|
||||||
|
return theStrategy.hessianNCycles()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun tolerg2(): Double {
|
||||||
|
return theStrategy.hessianG2Tolerance()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun tolerstp(): Double {
|
||||||
|
return theStrategy.hessianStepTolerance()
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class MnHesseFailedException(message: String?) : java.lang.Exception(message)
|
||||||
|
}
|
@ -0,0 +1,204 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import org.apache.commons.math3.linear.RealVector
|
||||||
|
import ru.inr.mass.minuit.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
internal object MnLineSearch {
|
||||||
|
fun search(
|
||||||
|
fcn: MnFcn,
|
||||||
|
st: MinimumParameters,
|
||||||
|
step: RealVector,
|
||||||
|
gdel: Double,
|
||||||
|
prec: MnMachinePrecision
|
||||||
|
): MnParabolaPoint {
|
||||||
|
var overal = 1000.0
|
||||||
|
var undral = -100.0
|
||||||
|
val toler = 0.05
|
||||||
|
var slamin = 0.0
|
||||||
|
val slambg = 5.0
|
||||||
|
val alpha = 2.0
|
||||||
|
val maxiter = 12
|
||||||
|
var niter = 0
|
||||||
|
for (i in 0 until step.getDimension()) {
|
||||||
|
if (abs(step.getEntry(i)) < prec.eps()) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
val ratio: Double = abs(st.vec().getEntry(i) / step.getEntry(i))
|
||||||
|
if (abs(slamin) < prec.eps()) {
|
||||||
|
slamin = ratio
|
||||||
|
}
|
||||||
|
if (ratio < slamin) {
|
||||||
|
slamin = ratio
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (abs(slamin) < prec.eps()) {
|
||||||
|
slamin = prec.eps()
|
||||||
|
}
|
||||||
|
slamin *= prec.eps2()
|
||||||
|
val F0: Double = st.fval()
|
||||||
|
val F1: Double = fcn.value(MnUtils.add(st.vec(), step))
|
||||||
|
var fvmin: Double = st.fval()
|
||||||
|
var xvmin = 0.0
|
||||||
|
if (F1 < F0) {
|
||||||
|
fvmin = F1
|
||||||
|
xvmin = 1.0
|
||||||
|
}
|
||||||
|
var toler8 = toler
|
||||||
|
var slamax = slambg
|
||||||
|
var flast = F1
|
||||||
|
var slam = 1.0
|
||||||
|
var iterate = false
|
||||||
|
var p0 = MnParabolaPoint(0.0, F0)
|
||||||
|
var p1 = MnParabolaPoint(slam, flast)
|
||||||
|
var F2 = 0.0
|
||||||
|
do {
|
||||||
|
// cut toler8 as function goes up
|
||||||
|
iterate = false
|
||||||
|
val pb: MnParabola = MnParabolaFactory.create(p0, gdel, p1)
|
||||||
|
var denom = 2.0 * (flast - F0 - gdel * slam) / (slam * slam)
|
||||||
|
if (abs(denom) < prec.eps()) {
|
||||||
|
denom = -0.1 * gdel
|
||||||
|
slam = 1.0
|
||||||
|
}
|
||||||
|
if (abs(denom) > prec.eps()) {
|
||||||
|
slam = -gdel / denom
|
||||||
|
}
|
||||||
|
if (slam < 0.0) {
|
||||||
|
slam = slamax
|
||||||
|
}
|
||||||
|
if (slam > slamax) {
|
||||||
|
slam = slamax
|
||||||
|
}
|
||||||
|
if (slam < toler8) {
|
||||||
|
slam = toler8
|
||||||
|
}
|
||||||
|
if (slam < slamin) {
|
||||||
|
return MnParabolaPoint(xvmin, fvmin)
|
||||||
|
}
|
||||||
|
if (abs(slam - 1.0) < toler8 && p1.y() < p0.y()) {
|
||||||
|
return MnParabolaPoint(xvmin, fvmin)
|
||||||
|
}
|
||||||
|
if (abs(slam - 1.0) < toler8) {
|
||||||
|
slam = 1.0 + toler8
|
||||||
|
}
|
||||||
|
F2 = fcn.value(MnUtils.add(st.vec(), MnUtils.mul(step, slam)))
|
||||||
|
if (F2 < fvmin) {
|
||||||
|
fvmin = F2
|
||||||
|
xvmin = slam
|
||||||
|
}
|
||||||
|
if (p0.y() - prec.eps() < fvmin && fvmin < p0.y() + prec.eps()) {
|
||||||
|
iterate = true
|
||||||
|
flast = F2
|
||||||
|
toler8 = toler * slam
|
||||||
|
overal = slam - toler8
|
||||||
|
slamax = overal
|
||||||
|
p1 = MnParabolaPoint(slam, flast)
|
||||||
|
niter++
|
||||||
|
}
|
||||||
|
} while (iterate && niter < maxiter)
|
||||||
|
if (niter >= maxiter) {
|
||||||
|
// exhausted max number of iterations
|
||||||
|
return MnParabolaPoint(xvmin, fvmin)
|
||||||
|
}
|
||||||
|
var p2 = MnParabolaPoint(slam, F2)
|
||||||
|
do {
|
||||||
|
slamax = max(slamax, alpha * abs(xvmin))
|
||||||
|
val pb: MnParabola = MnParabolaFactory.create(p0, p1, p2)
|
||||||
|
if (pb.a() < prec.eps2()) {
|
||||||
|
val slopem: Double = 2.0 * pb.a() * xvmin + pb.b()
|
||||||
|
slam = if (slopem < 0.0) {
|
||||||
|
xvmin + slamax
|
||||||
|
} else {
|
||||||
|
xvmin - slamax
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
slam = pb.min()
|
||||||
|
if (slam > xvmin + slamax) {
|
||||||
|
slam = xvmin + slamax
|
||||||
|
}
|
||||||
|
if (slam < xvmin - slamax) {
|
||||||
|
slam = xvmin - slamax
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (slam > 0.0) {
|
||||||
|
if (slam > overal) {
|
||||||
|
slam = overal
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (slam < undral) {
|
||||||
|
slam = undral
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var F3 = 0.0
|
||||||
|
do {
|
||||||
|
iterate = false
|
||||||
|
val toler9: Double = max(toler8, abs(toler8 * slam))
|
||||||
|
// min. of parabola at one point
|
||||||
|
if (abs(p0.x() - slam) < toler9 || abs(p1.x() - slam) < toler9 || abs(
|
||||||
|
p2.x() - slam) < toler9
|
||||||
|
) {
|
||||||
|
return MnParabolaPoint(xvmin, fvmin)
|
||||||
|
}
|
||||||
|
F3 = fcn.value(MnUtils.add(st.vec(), MnUtils.mul(step, slam)))
|
||||||
|
// if latest point worse than all three previous, cut step
|
||||||
|
if (F3 > p0.y() && F3 > p1.y() && F3 > p2.y()) {
|
||||||
|
if (slam > xvmin) {
|
||||||
|
overal = min(overal, slam - toler8)
|
||||||
|
}
|
||||||
|
if (slam < xvmin) {
|
||||||
|
undral = max(undral, slam + toler8)
|
||||||
|
}
|
||||||
|
slam = 0.5 * (slam + xvmin)
|
||||||
|
iterate = true
|
||||||
|
niter++
|
||||||
|
}
|
||||||
|
} while (iterate && niter < maxiter)
|
||||||
|
if (niter >= maxiter) {
|
||||||
|
// exhausted max number of iterations
|
||||||
|
return MnParabolaPoint(xvmin, fvmin)
|
||||||
|
}
|
||||||
|
|
||||||
|
// find worst previous point out of three and replace
|
||||||
|
val p3 = MnParabolaPoint(slam, F3)
|
||||||
|
if (p0.y() > p1.y() && p0.y() > p2.y()) {
|
||||||
|
p0 = p3
|
||||||
|
} else if (p1.y() > p0.y() && p1.y() > p2.y()) {
|
||||||
|
p1 = p3
|
||||||
|
} else {
|
||||||
|
p2 = p3
|
||||||
|
}
|
||||||
|
if (F3 < fvmin) {
|
||||||
|
fvmin = F3
|
||||||
|
xvmin = slam
|
||||||
|
} else {
|
||||||
|
if (slam > xvmin) {
|
||||||
|
overal = min(overal, slam - toler8)
|
||||||
|
}
|
||||||
|
if (slam < xvmin) {
|
||||||
|
undral = max(undral, slam + toler8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
niter++
|
||||||
|
} while (niter < maxiter)
|
||||||
|
return MnParabolaPoint(xvmin, fvmin)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the relative floating point arithmetic precision. The
|
||||||
|
* setPrecision() method can be used to override Minuit's own determination,
|
||||||
|
* when the user knows that the {FCN} function value is not calculated to the
|
||||||
|
* nominal machine accuracy.
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
* @author Darksnake
|
||||||
|
*/
|
||||||
|
class MnMachinePrecision internal constructor() {
|
||||||
|
private var theEpsMa2 = 0.0
|
||||||
|
private var theEpsMac = 0.0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* eps returns the smallest possible number so that 1.+eps > 1.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
fun eps(): Double {
|
||||||
|
return theEpsMac
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* eps2 returns 2*sqrt(eps)
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
fun eps2(): Double {
|
||||||
|
return theEpsMa2
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* override Minuit's own determination
|
||||||
|
*
|
||||||
|
* @param prec a double.
|
||||||
|
*/
|
||||||
|
fun setPrecision(prec: Double) {
|
||||||
|
theEpsMac = prec
|
||||||
|
theEpsMa2 = 2.0 * sqrt(theEpsMac)
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
setPrecision(4.0E-7)
|
||||||
|
var epstry = 0.5
|
||||||
|
val one = 1.0
|
||||||
|
for (i in 0..99) {
|
||||||
|
epstry *= 0.5
|
||||||
|
val epsp1 = one + epstry
|
||||||
|
val epsbak = epsp1 - one
|
||||||
|
if (epsbak < epstry) {
|
||||||
|
setPrecision(8.0 * epstry)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import ru.inr.mass.maths.MultiFunction
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MnMigrad provides minimization of the function by the method of MIGRAD, the
|
||||||
|
* most efficient and complete single method, recommended for general functions,
|
||||||
|
* and the functionality for parameters interaction. It also retains the result
|
||||||
|
* from the last minimization in case the user may want to do subsequent
|
||||||
|
* minimization steps with parameter interactions in between the minimization
|
||||||
|
* requests. The minimization produces as a by-product the error matrix of the
|
||||||
|
* parameters, which is usually reliable unless warning messages are produced.
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
* @author Darksnake
|
||||||
|
*/
|
||||||
|
class MnMigrad
|
||||||
|
/**
|
||||||
|
* construct from MultiFunction + MnUserParameterState + MnStrategy
|
||||||
|
*
|
||||||
|
* @param str a [hep.dataforge.MINUIT.MnStrategy] object.
|
||||||
|
* @param par a [hep.dataforge.MINUIT.MnUserParameterState] object.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
*/
|
||||||
|
(fcn: MultiFunction?, par: MnUserParameterState, str: MnStrategy) : MnApplication(fcn, par, str) {
|
||||||
|
private val theMinimizer: VariableMetricMinimizer = VariableMetricMinimizer()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from MultiFunction + double[] for parameters and errors
|
||||||
|
* with default strategy
|
||||||
|
*
|
||||||
|
* @param err an array of double.
|
||||||
|
* @param par an array of double.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
*/
|
||||||
|
constructor(fcn: MultiFunction?, par: DoubleArray, err: DoubleArray) : this(fcn, par, err, DEFAULT_STRATEGY)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from MultiFunction + double[] for parameters and errors
|
||||||
|
*
|
||||||
|
* @param stra a int.
|
||||||
|
* @param err an array of double.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
* @param par an array of double.
|
||||||
|
*/
|
||||||
|
constructor(fcn: MultiFunction?, par: DoubleArray, err: DoubleArray, stra: Int) : this(fcn,
|
||||||
|
MnUserParameterState(par, err),
|
||||||
|
MnStrategy(stra))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from MultiFunction + double[] for parameters and
|
||||||
|
* MnUserCovariance with default strategy
|
||||||
|
*
|
||||||
|
* @param cov a [hep.dataforge.MINUIT.MnUserCovariance] object.
|
||||||
|
* @param par an array of double.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
*/
|
||||||
|
constructor(fcn: MultiFunction?, par: DoubleArray, cov: MnUserCovariance) : this(fcn, par, cov, DEFAULT_STRATEGY)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from MultiFunction + double[] for parameters and
|
||||||
|
* MnUserCovariance
|
||||||
|
*
|
||||||
|
* @param stra a int.
|
||||||
|
* @param cov a [hep.dataforge.MINUIT.MnUserCovariance] object.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
* @param par an array of double.
|
||||||
|
*/
|
||||||
|
constructor(fcn: MultiFunction?, par: DoubleArray, cov: MnUserCovariance, stra: Int) : this(fcn,
|
||||||
|
MnUserParameterState(par, cov),
|
||||||
|
MnStrategy(stra))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from MultiFunction + MnUserParameters with default
|
||||||
|
* strategy
|
||||||
|
*
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
* @param par a [hep.dataforge.MINUIT.MnUserParameters] object.
|
||||||
|
*/
|
||||||
|
constructor(fcn: MultiFunction?, par: MnUserParameters) : this(fcn, par, DEFAULT_STRATEGY)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from MultiFunction + MnUserParameters
|
||||||
|
*
|
||||||
|
* @param stra a int.
|
||||||
|
* @param par a [hep.dataforge.MINUIT.MnUserParameters] object.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
*/
|
||||||
|
constructor(fcn: MultiFunction?, par: MnUserParameters, stra: Int) : this(fcn,
|
||||||
|
MnUserParameterState(par),
|
||||||
|
MnStrategy(stra))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from MultiFunction + MnUserParameters + MnUserCovariance
|
||||||
|
* with default strategy
|
||||||
|
*
|
||||||
|
* @param cov a [hep.dataforge.MINUIT.MnUserCovariance] object.
|
||||||
|
* @param par a [hep.dataforge.MINUIT.MnUserParameters] object.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
*/
|
||||||
|
constructor(fcn: MultiFunction?, par: MnUserParameters, cov: MnUserCovariance) : this(fcn,
|
||||||
|
par,
|
||||||
|
cov,
|
||||||
|
DEFAULT_STRATEGY)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from MultiFunction + MnUserParameters + MnUserCovariance
|
||||||
|
*
|
||||||
|
* @param stra a int.
|
||||||
|
* @param cov a [hep.dataforge.MINUIT.MnUserCovariance] object.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
* @param par a [hep.dataforge.MINUIT.MnUserParameters] object.
|
||||||
|
*/
|
||||||
|
constructor(fcn: MultiFunction?, par: MnUserParameters, cov: MnUserCovariance, stra: Int) : this(fcn,
|
||||||
|
MnUserParameterState(par, cov),
|
||||||
|
MnStrategy(stra))
|
||||||
|
|
||||||
|
override fun minimizer(): ModularFunctionMinimizer {
|
||||||
|
return theMinimizer
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,133 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import ru.inr.mass.maths.MultiFunction
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Causes minimization of the function by the method of MIGRAD, as does the
|
||||||
|
* MnMigrad class, but switches to the SIMPLEX method if MIGRAD fails to
|
||||||
|
* converge. Constructor arguments, methods arguments and names of methods are
|
||||||
|
* the same as for MnMigrad or MnSimplex.
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
* @author Darksnake
|
||||||
|
*/
|
||||||
|
class MnMinimize
|
||||||
|
/**
|
||||||
|
* construct from MultiFunction + MnUserParameterState + MnStrategy
|
||||||
|
*
|
||||||
|
* @param str a [hep.dataforge.MINUIT.MnStrategy] object.
|
||||||
|
* @param par a [hep.dataforge.MINUIT.MnUserParameterState] object.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
*/
|
||||||
|
(fcn: MultiFunction?, par: MnUserParameterState, str: MnStrategy) : MnApplication(fcn, par, str) {
|
||||||
|
private val theMinimizer: CombinedMinimizer = CombinedMinimizer()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from MultiFunction + double[] for parameters and errors
|
||||||
|
* with default strategy
|
||||||
|
*
|
||||||
|
* @param err an array of double.
|
||||||
|
* @param par an array of double.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
*/
|
||||||
|
constructor(fcn: MultiFunction?, par: DoubleArray, err: DoubleArray) : this(fcn, par, err, DEFAULT_STRATEGY)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from MultiFunction + double[] for parameters and errors
|
||||||
|
*
|
||||||
|
* @param stra a int.
|
||||||
|
* @param err an array of double.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
* @param par an array of double.
|
||||||
|
*/
|
||||||
|
constructor(fcn: MultiFunction?, par: DoubleArray, err: DoubleArray, stra: Int) : this(fcn,
|
||||||
|
MnUserParameterState(par, err),
|
||||||
|
MnStrategy(stra))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from MultiFunction + double[] for parameters and
|
||||||
|
* MnUserCovariance with default strategy
|
||||||
|
*
|
||||||
|
* @param cov a [hep.dataforge.MINUIT.MnUserCovariance] object.
|
||||||
|
* @param par an array of double.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
*/
|
||||||
|
constructor(fcn: MultiFunction?, par: DoubleArray, cov: MnUserCovariance) : this(fcn, par, cov, DEFAULT_STRATEGY)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from MultiFunction + double[] for parameters and
|
||||||
|
* MnUserCovariance
|
||||||
|
*
|
||||||
|
* @param stra a int.
|
||||||
|
* @param cov a [hep.dataforge.MINUIT.MnUserCovariance] object.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
* @param par an array of double.
|
||||||
|
*/
|
||||||
|
constructor(fcn: MultiFunction?, par: DoubleArray, cov: MnUserCovariance, stra: Int) : this(fcn,
|
||||||
|
MnUserParameterState(par, cov),
|
||||||
|
MnStrategy(stra))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from MultiFunction + MnUserParameters with default
|
||||||
|
* strategy
|
||||||
|
*
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
* @param par a [hep.dataforge.MINUIT.MnUserParameters] object.
|
||||||
|
*/
|
||||||
|
constructor(fcn: MultiFunction?, par: MnUserParameters) : this(fcn, par, DEFAULT_STRATEGY)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from MultiFunction + MnUserParameters
|
||||||
|
*
|
||||||
|
* @param stra a int.
|
||||||
|
* @param par a [hep.dataforge.MINUIT.MnUserParameters] object.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
*/
|
||||||
|
constructor(fcn: MultiFunction?, par: MnUserParameters, stra: Int) : this(fcn,
|
||||||
|
MnUserParameterState(par),
|
||||||
|
MnStrategy(stra))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from MultiFunction + MnUserParameters + MnUserCovariance
|
||||||
|
* with default strategy
|
||||||
|
*
|
||||||
|
* @param cov a [hep.dataforge.MINUIT.MnUserCovariance] object.
|
||||||
|
* @param par a [hep.dataforge.MINUIT.MnUserParameters] object.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
*/
|
||||||
|
constructor(fcn: MultiFunction?, par: MnUserParameters, cov: MnUserCovariance) : this(fcn,
|
||||||
|
par,
|
||||||
|
cov,
|
||||||
|
DEFAULT_STRATEGY)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from MultiFunction + MnUserParameters + MnUserCovariance
|
||||||
|
*
|
||||||
|
* @param stra a int.
|
||||||
|
* @param cov a [hep.dataforge.MINUIT.MnUserCovariance] object.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
* @param par a [hep.dataforge.MINUIT.MnUserParameters] object.
|
||||||
|
*/
|
||||||
|
constructor(fcn: MultiFunction?, par: MnUserParameters, cov: MnUserCovariance, stra: Int) : this(fcn,
|
||||||
|
MnUserParameterState(par, cov),
|
||||||
|
MnStrategy(stra))
|
||||||
|
|
||||||
|
override fun minimizer(): ModularFunctionMinimizer {
|
||||||
|
return theMinimizer
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,379 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import ru.inr.mass.maths.MultiFunction
|
||||||
|
import ru.inr.mass.minuit.*
|
||||||
|
import kotlin.jvm.JvmOverloads
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API class for Minos error analysis (asymmetric errors). Minimization has to
|
||||||
|
* be done before and minimum must be valid; possibility to ask only for one
|
||||||
|
* side of the Minos error;
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
* @author Darksnake
|
||||||
|
*/
|
||||||
|
class MnMinos(fcn: MultiFunction?, min: FunctionMinimum?, stra: MnStrategy?) {
|
||||||
|
private var theFCN: MultiFunction? = null
|
||||||
|
private var theMinimum: FunctionMinimum? = null
|
||||||
|
private var theStrategy: MnStrategy? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from FCN + minimum
|
||||||
|
*
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
* @param min a [hep.dataforge.MINUIT.FunctionMinimum] object.
|
||||||
|
*/
|
||||||
|
constructor(fcn: MultiFunction?, min: FunctionMinimum?) : this(fcn, min, MnApplication.DEFAULT_STRATEGY)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from FCN + minimum + strategy
|
||||||
|
*
|
||||||
|
* @param stra a int.
|
||||||
|
* @param min a [hep.dataforge.MINUIT.FunctionMinimum] object.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
*/
|
||||||
|
constructor(fcn: MultiFunction?, min: FunctionMinimum?, stra: Int) : this(fcn, min, MnStrategy(stra))
|
||||||
|
// public MnMinos(MultiFunction fcn, MnUserParameterState state, double errDef, MnStrategy stra) {
|
||||||
|
// theFCN = fcn;
|
||||||
|
// theStrategy = stra;
|
||||||
|
//
|
||||||
|
// MinimumState minState = null;
|
||||||
|
//
|
||||||
|
// MnUserTransformation transformation = state.getTransformation();
|
||||||
|
//
|
||||||
|
// MinimumSeed seed = new MinimumSeed(minState, transformation);
|
||||||
|
//
|
||||||
|
// theMinimum = new FunctionMinimum(seed,errDef);
|
||||||
|
// }
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* loval.
|
||||||
|
*
|
||||||
|
* @param par a int.
|
||||||
|
* @return a [hep.dataforge.MINUIT.MnCross] object.
|
||||||
|
*/
|
||||||
|
fun loval(par: Int): MnCross {
|
||||||
|
return loval(par, 1.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* loval.
|
||||||
|
*
|
||||||
|
* @param par a int.
|
||||||
|
* @param errDef a double.
|
||||||
|
* @return a [hep.dataforge.MINUIT.MnCross] object.
|
||||||
|
*/
|
||||||
|
fun loval(par: Int, errDef: Double): MnCross {
|
||||||
|
return loval(par, errDef, MnApplication.DEFAULT_MAXFCN)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* loval.
|
||||||
|
*
|
||||||
|
* @param par a int.
|
||||||
|
* @param errDef a double.
|
||||||
|
* @param maxcalls a int.
|
||||||
|
* @return a [hep.dataforge.MINUIT.MnCross] object.
|
||||||
|
*/
|
||||||
|
fun loval(par: Int, errDef: Double, maxcalls: Int): MnCross {
|
||||||
|
var errDef = errDef
|
||||||
|
var maxcalls = maxcalls
|
||||||
|
errDef *= theMinimum!!.errorDef()
|
||||||
|
assert(theMinimum!!.isValid())
|
||||||
|
assert(!theMinimum!!.userState().parameter(par).isFixed())
|
||||||
|
assert(!theMinimum!!.userState().parameter(par).isConst())
|
||||||
|
if (maxcalls == 0) {
|
||||||
|
val nvar: Int = theMinimum!!.userState().variableParameters()
|
||||||
|
maxcalls = 2 * (nvar + 1) * (200 + 100 * nvar + 5 * nvar * nvar)
|
||||||
|
}
|
||||||
|
val para = intArrayOf(par)
|
||||||
|
val upar: MnUserParameterState = theMinimum!!.userState().copy()
|
||||||
|
val err: Double = upar.error(par)
|
||||||
|
val `val`: Double = upar.value(par) - err
|
||||||
|
val xmid = doubleArrayOf(`val`)
|
||||||
|
val xdir = doubleArrayOf(-err)
|
||||||
|
val ind: Int = upar.intOfExt(par)
|
||||||
|
val m: MnAlgebraicSymMatrix = theMinimum!!.error().matrix()
|
||||||
|
val xunit: Double = sqrt(errDef / err)
|
||||||
|
for (i in 0 until m.nrow()) {
|
||||||
|
if (i == ind) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
val xdev: Double = xunit * m[ind, i]
|
||||||
|
val ext: Int = upar.extOfInt(i)
|
||||||
|
upar.setValue(ext, upar.value(ext) - xdev)
|
||||||
|
}
|
||||||
|
upar.fix(par)
|
||||||
|
upar.setValue(par, `val`)
|
||||||
|
val toler = 0.1
|
||||||
|
val cross = MnFunctionCross(theFCN, upar, theMinimum!!.fval(), theStrategy, errDef)
|
||||||
|
val aopt: MnCross = cross.cross(para, xmid, xdir, toler, maxcalls)
|
||||||
|
if (aopt.atLimit()) {
|
||||||
|
MINUITPlugin.logStatic("MnMinos parameter $par is at lower limit.")
|
||||||
|
}
|
||||||
|
if (aopt.atMaxFcn()) {
|
||||||
|
MINUITPlugin.logStatic("MnMinos maximum number of function calls exceeded for parameter $par")
|
||||||
|
}
|
||||||
|
if (aopt.newMinimum()) {
|
||||||
|
MINUITPlugin.logStatic("MnMinos new minimum found while looking for parameter $par")
|
||||||
|
}
|
||||||
|
if (!aopt.isValid()) {
|
||||||
|
MINUITPlugin.logStatic("MnMinos could not find lower value for parameter $par.")
|
||||||
|
}
|
||||||
|
return aopt
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* calculate one side (negative or positive error) of the parameter
|
||||||
|
*
|
||||||
|
* @param maxcalls a int.
|
||||||
|
* @param par a int.
|
||||||
|
* @param errDef a double.
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* lower.
|
||||||
|
*
|
||||||
|
* @param par a int.
|
||||||
|
* @param errDef a double.
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* lower.
|
||||||
|
*
|
||||||
|
* @param par a int.
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
@JvmOverloads
|
||||||
|
fun lower(par: Int, errDef: Double = 1.0, maxcalls: Int = MnApplication.DEFAULT_MAXFCN): Double {
|
||||||
|
val upar: MnUserParameterState = theMinimum!!.userState()
|
||||||
|
val err: Double = theMinimum!!.userState().error(par)
|
||||||
|
val aopt: MnCross = loval(par, errDef, maxcalls)
|
||||||
|
return if (aopt.isValid()) -1.0 * err * (1.0 + aopt.value()) else if (aopt.atLimit()) upar.parameter(par)
|
||||||
|
.lowerLimit() else upar.value(par)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* minos.
|
||||||
|
*
|
||||||
|
* @param par a int.
|
||||||
|
* @return a [hep.dataforge.MINUIT.MinosError] object.
|
||||||
|
*/
|
||||||
|
fun minos(par: Int): MinosError {
|
||||||
|
return minos(par, 1.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* minos.
|
||||||
|
*
|
||||||
|
* @param par a int.
|
||||||
|
* @param errDef a double.
|
||||||
|
* @return a [hep.dataforge.MINUIT.MinosError] object.
|
||||||
|
*/
|
||||||
|
fun minos(par: Int, errDef: Double): MinosError {
|
||||||
|
return minos(par, errDef, MnApplication.DEFAULT_MAXFCN)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Causes a MINOS error analysis to be performed on the parameter whose
|
||||||
|
* number is specified. MINOS errors may be expensive to calculate, but are
|
||||||
|
* very reliable since they take account of non-linearities in the problem
|
||||||
|
* as well as parameter correlations, and are in general asymmetric.
|
||||||
|
*
|
||||||
|
* @param maxcalls Specifies the (approximate) maximum number of function
|
||||||
|
* calls per parameter requested, after which the calculation will be
|
||||||
|
* stopped for that parameter.
|
||||||
|
* @param errDef a double.
|
||||||
|
* @param par a int.
|
||||||
|
* @return a [hep.dataforge.MINUIT.MinosError] object.
|
||||||
|
*/
|
||||||
|
fun minos(par: Int, errDef: Double, maxcalls: Int): MinosError {
|
||||||
|
assert(theMinimum!!.isValid())
|
||||||
|
assert(!theMinimum!!.userState().parameter(par).isFixed())
|
||||||
|
assert(!theMinimum!!.userState().parameter(par).isConst())
|
||||||
|
val up: MnCross = upval(par, errDef, maxcalls)
|
||||||
|
val lo: MnCross = loval(par, errDef, maxcalls)
|
||||||
|
return MinosError(par, theMinimum!!.userState().value(par), lo, up)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* range.
|
||||||
|
*
|
||||||
|
* @param par a int.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
fun range(par: Int): Range {
|
||||||
|
return range(par, 1.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* range.
|
||||||
|
*
|
||||||
|
* @param par a int.
|
||||||
|
* @param errDef a double.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
fun range(par: Int, errDef: Double): Range {
|
||||||
|
return range(par, errDef, MnApplication.DEFAULT_MAXFCN)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Causes a MINOS error analysis for external parameter n.
|
||||||
|
*
|
||||||
|
* @param maxcalls a int.
|
||||||
|
* @param errDef a double.
|
||||||
|
* @return The lower and upper bounds of parameter
|
||||||
|
* @param par a int.
|
||||||
|
*/
|
||||||
|
fun range(par: Int, errDef: Double, maxcalls: Int): Range {
|
||||||
|
val mnerr: MinosError = minos(par, errDef, maxcalls)
|
||||||
|
return mnerr.range()
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* upper.
|
||||||
|
*
|
||||||
|
* @param par a int.
|
||||||
|
* @param errDef a double.
|
||||||
|
* @param maxcalls a int.
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* upper.
|
||||||
|
*
|
||||||
|
* @param par a int.
|
||||||
|
* @param errDef a double.
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* upper.
|
||||||
|
*
|
||||||
|
* @param par a int.
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
@JvmOverloads
|
||||||
|
fun upper(par: Int, errDef: Double = 1.0, maxcalls: Int = MnApplication.DEFAULT_MAXFCN): Double {
|
||||||
|
val upar: MnUserParameterState = theMinimum!!.userState()
|
||||||
|
val err: Double = theMinimum!!.userState().error(par)
|
||||||
|
val aopt: MnCross = upval(par, errDef, maxcalls)
|
||||||
|
return if (aopt.isValid()) err * (1.0 + aopt.value()) else if (aopt.atLimit()) upar.parameter(par)
|
||||||
|
.upperLimit() else upar.value(par)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* upval.
|
||||||
|
*
|
||||||
|
* @param par a int.
|
||||||
|
* @return a [hep.dataforge.MINUIT.MnCross] object.
|
||||||
|
*/
|
||||||
|
fun upval(par: Int): MnCross {
|
||||||
|
return upval(par, 1.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* upval.
|
||||||
|
*
|
||||||
|
* @param par a int.
|
||||||
|
* @param errDef a double.
|
||||||
|
* @return a [hep.dataforge.MINUIT.MnCross] object.
|
||||||
|
*/
|
||||||
|
fun upval(par: Int, errDef: Double): MnCross {
|
||||||
|
return upval(par, errDef, MnApplication.DEFAULT_MAXFCN)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* upval.
|
||||||
|
*
|
||||||
|
* @param par a int.
|
||||||
|
* @param errDef a double.
|
||||||
|
* @param maxcalls a int.
|
||||||
|
* @return a [hep.dataforge.MINUIT.MnCross] object.
|
||||||
|
*/
|
||||||
|
fun upval(par: Int, errDef: Double, maxcalls: Int): MnCross {
|
||||||
|
var errDef = errDef
|
||||||
|
var maxcalls = maxcalls
|
||||||
|
errDef *= theMinimum!!.errorDef()
|
||||||
|
assert(theMinimum!!.isValid())
|
||||||
|
assert(!theMinimum!!.userState().parameter(par).isFixed())
|
||||||
|
assert(!theMinimum!!.userState().parameter(par).isConst())
|
||||||
|
if (maxcalls == 0) {
|
||||||
|
val nvar: Int = theMinimum!!.userState().variableParameters()
|
||||||
|
maxcalls = 2 * (nvar + 1) * (200 + 100 * nvar + 5 * nvar * nvar)
|
||||||
|
}
|
||||||
|
val para = intArrayOf(par)
|
||||||
|
val upar: MnUserParameterState = theMinimum!!.userState().copy()
|
||||||
|
val err: Double = upar.error(par)
|
||||||
|
val `val`: Double = upar.value(par) + err
|
||||||
|
val xmid = doubleArrayOf(`val`)
|
||||||
|
val xdir = doubleArrayOf(err)
|
||||||
|
val ind: Int = upar.intOfExt(par)
|
||||||
|
val m: MnAlgebraicSymMatrix = theMinimum!!.error().matrix()
|
||||||
|
val xunit: Double = sqrt(errDef / err)
|
||||||
|
for (i in 0 until m.nrow()) {
|
||||||
|
if (i == ind) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
val xdev: Double = xunit * m[ind, i]
|
||||||
|
val ext: Int = upar.extOfInt(i)
|
||||||
|
upar.setValue(ext, upar.value(ext) + xdev)
|
||||||
|
}
|
||||||
|
upar.fix(par)
|
||||||
|
upar.setValue(par, `val`)
|
||||||
|
val toler = 0.1
|
||||||
|
val cross = MnFunctionCross(theFCN, upar, theMinimum!!.fval(), theStrategy, errDef)
|
||||||
|
val aopt: MnCross = cross.cross(para, xmid, xdir, toler, maxcalls)
|
||||||
|
if (aopt.atLimit()) {
|
||||||
|
MINUITPlugin.logStatic("MnMinos parameter $par is at upper limit.")
|
||||||
|
}
|
||||||
|
if (aopt.atMaxFcn()) {
|
||||||
|
MINUITPlugin.logStatic("MnMinos maximum number of function calls exceeded for parameter $par")
|
||||||
|
}
|
||||||
|
if (aopt.newMinimum()) {
|
||||||
|
MINUITPlugin.logStatic("MnMinos new minimum found while looking for parameter $par")
|
||||||
|
}
|
||||||
|
if (!aopt.isValid()) {
|
||||||
|
MINUITPlugin.logStatic("MnMinos could not find upper value for parameter $par.")
|
||||||
|
}
|
||||||
|
return aopt
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from FCN + minimum + strategy
|
||||||
|
*
|
||||||
|
* @param stra a [hep.dataforge.MINUIT.MnStrategy] object.
|
||||||
|
* @param min a [hep.dataforge.MINUIT.FunctionMinimum] object.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
*/
|
||||||
|
init {
|
||||||
|
theFCN = fcn
|
||||||
|
theMinimum = min
|
||||||
|
theStrategy = stra
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
/**
|
||||||
|
* parabola = a*xx + b*x + c
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
internal class MnParabola(private val theA: Double, private val theB: Double, private val theC: Double) {
|
||||||
|
fun a(): Double {
|
||||||
|
return theA
|
||||||
|
}
|
||||||
|
|
||||||
|
fun b(): Double {
|
||||||
|
return theB
|
||||||
|
}
|
||||||
|
|
||||||
|
fun c(): Double {
|
||||||
|
return theC
|
||||||
|
}
|
||||||
|
|
||||||
|
fun min(): Double {
|
||||||
|
return -theB / (2.0 * theA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun x_neg(y: Double): Double {
|
||||||
|
return -sqrt(y / theA + min() * min() - theC / theA) + min()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun x_pos(y: Double): Double {
|
||||||
|
return sqrt(y / theA + min() * min() - theC / theA) + min()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun y(x: Double): Double {
|
||||||
|
return theA * x * x + theB * x + theC
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ymin(): Double {
|
||||||
|
return -theB * theB / (4.0 * theA) + theC
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
internal object MnParabolaFactory {
|
||||||
|
fun create(p1: MnParabolaPoint, p2: MnParabolaPoint, p3: MnParabolaPoint): MnParabola {
|
||||||
|
var x1: Double = p1.x()
|
||||||
|
var x2: Double = p2.x()
|
||||||
|
var x3: Double = p3.x()
|
||||||
|
val dx12 = x1 - x2
|
||||||
|
val dx13 = x1 - x3
|
||||||
|
val dx23 = x2 - x3
|
||||||
|
val xm = (x1 + x2 + x3) / 3.0
|
||||||
|
x1 -= xm
|
||||||
|
x2 -= xm
|
||||||
|
x3 -= xm
|
||||||
|
val y1: Double = p1.y()
|
||||||
|
val y2: Double = p2.y()
|
||||||
|
val y3: Double = p3.y()
|
||||||
|
val a = y1 / (dx12 * dx13) - y2 / (dx12 * dx23) + y3 / (dx13 * dx23)
|
||||||
|
var b = -y1 * (x2 + x3) / (dx12 * dx13) + y2 * (x1 + x3) / (dx12 * dx23) - y3 * (x1 + x2) / (dx13 * dx23)
|
||||||
|
var c = y1 - a * x1 * x1 - b * x1
|
||||||
|
c += xm * (xm * a - b)
|
||||||
|
b -= 2.0 * xm * a
|
||||||
|
return MnParabola(a, b, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun create(p1: MnParabolaPoint, dxdy1: Double, p2: MnParabolaPoint): MnParabola {
|
||||||
|
val x1: Double = p1.x()
|
||||||
|
val xx1 = x1 * x1
|
||||||
|
val x2: Double = p2.x()
|
||||||
|
val xx2 = x2 * x2
|
||||||
|
val y1: Double = p1.y()
|
||||||
|
val y12: Double = p1.y() - p2.y()
|
||||||
|
val det = xx1 - xx2 - 2.0 * x1 * (x1 - x2)
|
||||||
|
val a = -(y12 + (x2 - x1) * dxdy1) / det
|
||||||
|
val b = -(-2.0 * x1 * y12 + (xx1 - xx2) * dxdy1) / det
|
||||||
|
val c = y1 - a * xx1 - b * x1
|
||||||
|
return MnParabola(a, b, c)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
internal class MnParabolaPoint(private val theX: Double, private val theY: Double) {
|
||||||
|
fun x(): Double {
|
||||||
|
return theX
|
||||||
|
}
|
||||||
|
|
||||||
|
fun y(): Double {
|
||||||
|
return theY
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import ru.inr.mass.maths.MultiFunction
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scans the values of FCN as a function of one parameter and retains the best
|
||||||
|
* function and parameter values found
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
internal class MnParameterScan {
|
||||||
|
private var theAmin: Double
|
||||||
|
private var theFCN: MultiFunction?
|
||||||
|
private var theParameters: MnUserParameters
|
||||||
|
|
||||||
|
constructor(fcn: MultiFunction, par: MnUserParameters) {
|
||||||
|
theFCN = fcn
|
||||||
|
theParameters = par
|
||||||
|
theAmin = fcn.value(par.params())
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(fcn: MultiFunction?, par: MnUserParameters, fval: Double) {
|
||||||
|
theFCN = fcn
|
||||||
|
theParameters = par
|
||||||
|
theAmin = fval
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fval(): Double {
|
||||||
|
return theAmin
|
||||||
|
}
|
||||||
|
|
||||||
|
fun parameters(): MnUserParameters {
|
||||||
|
return theParameters
|
||||||
|
}
|
||||||
|
|
||||||
|
fun scan(par: Int): List<Range> {
|
||||||
|
return scan(par, 41)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun scan(par: Int, maxsteps: Int): List<Range> {
|
||||||
|
return scan(par, maxsteps, 0.0, 0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns pairs of (x,y) points, x=parameter value, y=function value of FCN
|
||||||
|
* @param high
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
fun scan(par: Int, maxsteps: Int, low: Double, high: Double): List<Range> {
|
||||||
|
var maxsteps = maxsteps
|
||||||
|
var low = low
|
||||||
|
var high = high
|
||||||
|
if (maxsteps > 101) {
|
||||||
|
maxsteps = 101
|
||||||
|
}
|
||||||
|
val result: MutableList<Range> = java.util.ArrayList<Range>(maxsteps + 1)
|
||||||
|
val params: DoubleArray = theParameters.params()
|
||||||
|
result.add(Range(params[par], theAmin))
|
||||||
|
if (low > high) {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
if (maxsteps < 2) {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
if (low == 0.0 && high == 0.0) {
|
||||||
|
low = params[par] - 2.0 * theParameters.error(par)
|
||||||
|
high = params[par] + 2.0 * theParameters.error(par)
|
||||||
|
}
|
||||||
|
if (low == 0.0 && high == 0.0 && theParameters.parameter(par).hasLimits()) {
|
||||||
|
if (theParameters.parameter(par).hasLowerLimit()) {
|
||||||
|
low = theParameters.parameter(par).lowerLimit()
|
||||||
|
}
|
||||||
|
if (theParameters.parameter(par).hasUpperLimit()) {
|
||||||
|
high = theParameters.parameter(par).upperLimit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (theParameters.parameter(par).hasLimits()) {
|
||||||
|
if (theParameters.parameter(par).hasLowerLimit()) {
|
||||||
|
low = max(low, theParameters.parameter(par).lowerLimit())
|
||||||
|
}
|
||||||
|
if (theParameters.parameter(par).hasUpperLimit()) {
|
||||||
|
high = min(high, theParameters.parameter(par).upperLimit())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val x0 = low
|
||||||
|
val stp = (high - low) / (maxsteps - 1.0)
|
||||||
|
for (i in 0 until maxsteps) {
|
||||||
|
params[par] = x0 + i.toDouble() * stp
|
||||||
|
val fval: Double = theFCN.value(params)
|
||||||
|
if (fval < theAmin) {
|
||||||
|
theParameters.setValue(par, params[par])
|
||||||
|
theAmin = fval
|
||||||
|
}
|
||||||
|
result.add(Range(params[par], fval))
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,438 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import java.lang.StringBuffer
|
||||||
|
import kotlin.jvm.JvmOverloads
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MnPlot produces a text-screen graphical output of (x,y) points. E.g. from
|
||||||
|
* Scan or Contours.
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
* @author Darksnake
|
||||||
|
*/
|
||||||
|
class MnPlot @JvmOverloads constructor(private val thePageWidth: Int = 80, private val thePageLength: Int = 30) {
|
||||||
|
private var bh = 0.0
|
||||||
|
private var bl = 0.0
|
||||||
|
private var bwid = 0.0
|
||||||
|
private var nb = 0
|
||||||
|
fun length(): Int {
|
||||||
|
return thePageLength
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun mnbins(a1: Double, a2: Double, naa: Int) {
|
||||||
|
|
||||||
|
//*-*-*-*-*-*-*-*-*-*-*Compute reasonable histogram intervals*-*-*-*-*-*-*-*-*
|
||||||
|
//*-* ======================================
|
||||||
|
//*-* Function TO DETERMINE REASONABLE HISTOGRAM INTERVALS
|
||||||
|
//*-* GIVEN ABSOLUTE UPPER AND LOWER BOUNDS A1 AND A2
|
||||||
|
//*-* AND DESIRED MAXIMUM NUMBER OF BINS NAA
|
||||||
|
//*-* PROGRAM MAKES REASONABLE BINNING FROM BL TO BH OF WIDTH BWID
|
||||||
|
//*-* F. JAMES, AUGUST, 1974 , stolen for Minuit, 1988
|
||||||
|
//*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
|
||||||
|
|
||||||
|
/* Local variables */
|
||||||
|
var awid: Double
|
||||||
|
var ah: Double
|
||||||
|
var sigfig: Double
|
||||||
|
var sigrnd: Double
|
||||||
|
var alb: Double
|
||||||
|
var kwid: Int
|
||||||
|
var lwid: Int
|
||||||
|
var na = 0
|
||||||
|
var log_: Int
|
||||||
|
val al: Double = if (a1 < a2) a1 else a2
|
||||||
|
ah = if (a1 > a2) a1 else a2
|
||||||
|
if (al == ah) {
|
||||||
|
ah = al + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
//*-*- IF NAA .EQ. -1 , PROGRAM USES BWID INPUT FROM CALLING ROUTINE
|
||||||
|
var skip = naa == -1 && bwid > 0
|
||||||
|
if (!skip) {
|
||||||
|
na = naa - 1
|
||||||
|
if (na < 1) {
|
||||||
|
na = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (true) {
|
||||||
|
if (!skip) {
|
||||||
|
//*-*- GET NOMINAL BIN WIDTH IN EXPON FORM
|
||||||
|
awid = (ah - al) / na.toDouble()
|
||||||
|
log_ = log10(awid)
|
||||||
|
if (awid <= 1) {
|
||||||
|
--log_
|
||||||
|
}
|
||||||
|
sigfig = awid * pow(10.0, -log_.toDouble())
|
||||||
|
//*-*- ROUND MANTISSA UP TO 2, 2.5, 5, OR 10
|
||||||
|
if (sigfig <= 2) {
|
||||||
|
sigrnd = 2.0
|
||||||
|
} else if (sigfig <= 2.5) {
|
||||||
|
sigrnd = 2.5
|
||||||
|
} else if (sigfig <= 5) {
|
||||||
|
sigrnd = 5.0
|
||||||
|
} else {
|
||||||
|
sigrnd = 1.0
|
||||||
|
++log_
|
||||||
|
}
|
||||||
|
bwid = sigrnd * pow(10.0, log_.toDouble())
|
||||||
|
}
|
||||||
|
alb = al / bwid
|
||||||
|
lwid = alb.toInt()
|
||||||
|
if (alb < 0) {
|
||||||
|
--lwid
|
||||||
|
}
|
||||||
|
bl = bwid * lwid.toDouble()
|
||||||
|
alb = ah / bwid + 1
|
||||||
|
kwid = alb.toInt()
|
||||||
|
if (alb < 0) {
|
||||||
|
--kwid
|
||||||
|
}
|
||||||
|
bh = bwid * kwid.toDouble()
|
||||||
|
nb = kwid - lwid
|
||||||
|
if (naa <= 5) {
|
||||||
|
if (naa == -1) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//*-*- REQUEST FOR ONE BIN IS DIFFICULT CASE
|
||||||
|
if (naa > 1 || nb == 1) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
bwid *= 2.0
|
||||||
|
nb = 1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (nb shl 1 != naa) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
++na
|
||||||
|
skip = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun mnplot(xpt: DoubleArray, ypt: DoubleArray, chpt: StringBuffer, nxypt: Int, npagwd: Int, npagln: Int) {
|
||||||
|
//*-*-*-*Plots points in array xypt onto one page with labelled axes*-*-*-*-*
|
||||||
|
//*-* ===========================================================
|
||||||
|
//*-* NXYPT is the number of points to be plotted
|
||||||
|
//*-* XPT(I) = x-coord. of ith point
|
||||||
|
//*-* YPT(I) = y-coord. of ith point
|
||||||
|
//*-* CHPT(I) = character to be plotted at this position
|
||||||
|
//*-* the input point arrays XPT, YPT, CHPT are destroyed.
|
||||||
|
//*-*
|
||||||
|
//*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
|
||||||
|
|
||||||
|
/* Local variables */
|
||||||
|
var xmin: Double
|
||||||
|
var xmax: Double
|
||||||
|
var ymax: Double
|
||||||
|
var savx: Double
|
||||||
|
var savy: Double
|
||||||
|
var yprt: Double
|
||||||
|
var xbest: Double
|
||||||
|
var ybest: Double
|
||||||
|
val xvalus = DoubleArray(12)
|
||||||
|
val any: Double
|
||||||
|
val iten: Int
|
||||||
|
var j: Int
|
||||||
|
var k: Int
|
||||||
|
var maxnx: Int
|
||||||
|
var maxny: Int
|
||||||
|
var iquit: Int
|
||||||
|
var ni: Int
|
||||||
|
var linodd: Int
|
||||||
|
var ibk: Int
|
||||||
|
var isp1: Int
|
||||||
|
var ks: Int
|
||||||
|
var ix: Int
|
||||||
|
var overpr: Boolean
|
||||||
|
val cline = StringBuffer(npagwd)
|
||||||
|
for (ii in 0 until npagwd) {
|
||||||
|
cline.append(' ')
|
||||||
|
}
|
||||||
|
var chsav: Char
|
||||||
|
val chbest: Char
|
||||||
|
|
||||||
|
/* Function Body */
|
||||||
|
//*-* Computing MIN
|
||||||
|
maxnx = if (npagwd - 20 < 100) npagwd - 20 else 100
|
||||||
|
if (maxnx < 10) {
|
||||||
|
maxnx = 10
|
||||||
|
}
|
||||||
|
maxny = npagln
|
||||||
|
if (maxny < 10) {
|
||||||
|
maxny = 10
|
||||||
|
}
|
||||||
|
if (nxypt <= 1) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
xbest = xpt[0]
|
||||||
|
ybest = ypt[0]
|
||||||
|
chbest = chpt.get(0)
|
||||||
|
//*-*- order the points by decreasing y
|
||||||
|
val km1: Int = nxypt - 1
|
||||||
|
var i: Int = 1
|
||||||
|
while (i <= km1) {
|
||||||
|
iquit = 0
|
||||||
|
ni = nxypt - i
|
||||||
|
j = 1
|
||||||
|
while (j <= ni) {
|
||||||
|
if (ypt[j - 1] > ypt[j]) {
|
||||||
|
++j
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
savx = xpt[j - 1]
|
||||||
|
xpt[j - 1] = xpt[j]
|
||||||
|
xpt[j] = savx
|
||||||
|
savy = ypt[j - 1]
|
||||||
|
ypt[j - 1] = ypt[j]
|
||||||
|
ypt[j] = savy
|
||||||
|
chsav = chpt.get(j - 1)
|
||||||
|
chpt.setCharAt(j - 1, chpt.get(j))
|
||||||
|
chpt.setCharAt(j, chsav)
|
||||||
|
iquit = 1
|
||||||
|
++j
|
||||||
|
}
|
||||||
|
if (iquit == 0) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
++i
|
||||||
|
}
|
||||||
|
//*-*- find extreme values
|
||||||
|
xmax = xpt[0]
|
||||||
|
xmin = xmax
|
||||||
|
i = 1
|
||||||
|
while (i <= nxypt) {
|
||||||
|
if (xpt[i - 1] > xmax) {
|
||||||
|
xmax = xpt[i - 1]
|
||||||
|
}
|
||||||
|
if (xpt[i - 1] < xmin) {
|
||||||
|
xmin = xpt[i - 1]
|
||||||
|
}
|
||||||
|
++i
|
||||||
|
}
|
||||||
|
val dxx: Double = (xmax - xmin) * .001
|
||||||
|
xmax += dxx
|
||||||
|
xmin -= dxx
|
||||||
|
mnbins(xmin, xmax, maxnx)
|
||||||
|
xmin = bl
|
||||||
|
xmax = bh
|
||||||
|
var nx: Int = nb
|
||||||
|
val bwidx: Double = bwid
|
||||||
|
ymax = ypt[0]
|
||||||
|
var ymin: Double = ypt[nxypt - 1]
|
||||||
|
if (ymax == ymin) {
|
||||||
|
ymax = ymin + 1
|
||||||
|
}
|
||||||
|
val dyy: Double = (ymax - ymin) * .001
|
||||||
|
ymax += dyy
|
||||||
|
ymin -= dyy
|
||||||
|
mnbins(ymin, ymax, maxny)
|
||||||
|
ymin = bl
|
||||||
|
ymax = bh
|
||||||
|
var ny: Int = nb
|
||||||
|
val bwidy: Double = bwid
|
||||||
|
any = ny.toDouble()
|
||||||
|
//*-*- if first point is blank, it is an 'origin'
|
||||||
|
if (chbest != ' ') {
|
||||||
|
xbest = (xmax + xmin) * .5
|
||||||
|
ybest = (ymax + ymin) * .5
|
||||||
|
}
|
||||||
|
//*-*- find scale constants
|
||||||
|
val ax: Double = 1 / bwidx
|
||||||
|
val ay: Double = 1 / bwidy
|
||||||
|
val bx: Double = -ax * xmin + 2
|
||||||
|
val by: Double = -ay * ymin - 2
|
||||||
|
//*-*- convert points to grid positions
|
||||||
|
i = 1
|
||||||
|
while (i <= nxypt) {
|
||||||
|
xpt[i - 1] = ax * xpt[i - 1] + bx
|
||||||
|
ypt[i - 1] = any - ay * ypt[i - 1] - by
|
||||||
|
++i
|
||||||
|
}
|
||||||
|
val nxbest: Int = (ax * xbest + bx).toInt()
|
||||||
|
val nybest: Int = (any - ay * ybest - by).toInt()
|
||||||
|
//*-*- print the points
|
||||||
|
ny += 2
|
||||||
|
nx += 2
|
||||||
|
isp1 = 1
|
||||||
|
linodd = 1
|
||||||
|
overpr = false
|
||||||
|
i = 1
|
||||||
|
while (i <= ny) {
|
||||||
|
ibk = 1
|
||||||
|
while (ibk <= nx) {
|
||||||
|
cline.setCharAt(ibk - 1, ' ')
|
||||||
|
++ibk
|
||||||
|
}
|
||||||
|
// cline.setCharAt(nx,'\0');
|
||||||
|
// cline.setCharAt(nx+1,'\0');
|
||||||
|
cline.setCharAt(0, '.')
|
||||||
|
cline.setCharAt(nx - 1, '.')
|
||||||
|
cline.setCharAt(nxbest - 1, '.')
|
||||||
|
if (i == 1 || i == nybest || i == ny) {
|
||||||
|
j = 1
|
||||||
|
while (j <= nx) {
|
||||||
|
cline.setCharAt(j - 1, '.')
|
||||||
|
++j
|
||||||
|
}
|
||||||
|
}
|
||||||
|
yprt = ymax - (i - 1.0) * bwidy
|
||||||
|
var isplset = false
|
||||||
|
if (isp1 <= nxypt) {
|
||||||
|
//*-*- find the points to be plotted on this line
|
||||||
|
k = isp1
|
||||||
|
while (k <= nxypt) {
|
||||||
|
ks = ypt[k - 1].toInt()
|
||||||
|
if (ks > i) {
|
||||||
|
isp1 = k
|
||||||
|
isplset = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
ix = xpt[k - 1].toInt()
|
||||||
|
if (cline.get(ix - 1) != '.' && cline.get(ix - 1) != ' ') {
|
||||||
|
if (cline.get(ix - 1) == chpt.get(k - 1)) {
|
||||||
|
++k
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
overpr = true
|
||||||
|
//*-*- OVERPR is true if one or more positions contains more than
|
||||||
|
//*-*- one point
|
||||||
|
cline.setCharAt(ix - 1, '&')
|
||||||
|
++k
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cline.setCharAt(ix - 1, chpt.get(k - 1))
|
||||||
|
++k
|
||||||
|
}
|
||||||
|
if (!isplset) {
|
||||||
|
isp1 = nxypt + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (linodd != 1 && i != ny) {
|
||||||
|
linodd = 1
|
||||||
|
java.lang.System.out.printf(" %s", cline.substring(0, 60))
|
||||||
|
} else {
|
||||||
|
java.lang.System.out.printf(" %14.7g ..%s", yprt, cline.substring(0, 60))
|
||||||
|
linodd = 0
|
||||||
|
}
|
||||||
|
println()
|
||||||
|
++i
|
||||||
|
}
|
||||||
|
//*-*- print labels on x-axis every ten columns
|
||||||
|
ibk = 1
|
||||||
|
while (ibk <= nx) {
|
||||||
|
cline.setCharAt(ibk - 1, ' ')
|
||||||
|
if (ibk % 10 == 1) {
|
||||||
|
cline.setCharAt(ibk - 1, '/')
|
||||||
|
}
|
||||||
|
++ibk
|
||||||
|
}
|
||||||
|
java.lang.System.out.printf(" %s", cline)
|
||||||
|
java.lang.System.out.printf("\n")
|
||||||
|
ibk = 1
|
||||||
|
while (ibk <= 12) {
|
||||||
|
xvalus[ibk - 1] = xmin + (ibk - 1.0) * 10 * bwidx
|
||||||
|
++ibk
|
||||||
|
}
|
||||||
|
java.lang.System.out.printf(" ")
|
||||||
|
iten = (nx + 9) / 10
|
||||||
|
ibk = 1
|
||||||
|
while (ibk <= iten) {
|
||||||
|
java.lang.System.out.printf(" %9.4g", xvalus[ibk - 1])
|
||||||
|
++ibk
|
||||||
|
}
|
||||||
|
java.lang.System.out.printf("\n")
|
||||||
|
if (overpr) {
|
||||||
|
val chmess = " Overprint character is &"
|
||||||
|
java.lang.System.out.printf(" ONE COLUMN=%13.7g%s", bwidx, chmess)
|
||||||
|
} else {
|
||||||
|
val chmess = " "
|
||||||
|
java.lang.System.out.printf(" ONE COLUMN=%13.7g%s", bwidx, chmess)
|
||||||
|
}
|
||||||
|
println()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* plot.
|
||||||
|
*
|
||||||
|
* @param points a [List] object.
|
||||||
|
*/
|
||||||
|
fun plot(points: List<Range>) {
|
||||||
|
val x = DoubleArray(points.size)
|
||||||
|
val y = DoubleArray(points.size)
|
||||||
|
val chpt = StringBuffer(points.size)
|
||||||
|
for ((i, ipoint) in points.withIndex()) {
|
||||||
|
x[i] = ipoint.getFirst()
|
||||||
|
y[i] = ipoint.getSecond()
|
||||||
|
chpt.append('*')
|
||||||
|
}
|
||||||
|
mnplot(x, y, chpt, points.size, width(), length())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* plot.
|
||||||
|
*
|
||||||
|
* @param xmin a double.
|
||||||
|
* @param ymin a double.
|
||||||
|
* @param points a [List] object.
|
||||||
|
*/
|
||||||
|
fun plot(xmin: Double, ymin: Double, points: List<Range>) {
|
||||||
|
val x = DoubleArray(points.size + 2)
|
||||||
|
x[0] = xmin
|
||||||
|
x[1] = xmin
|
||||||
|
val y = DoubleArray(points.size + 2)
|
||||||
|
y[0] = ymin
|
||||||
|
y[1] = ymin
|
||||||
|
val chpt = StringBuffer(points.size + 2)
|
||||||
|
chpt.append(' ')
|
||||||
|
chpt.append('X')
|
||||||
|
var i = 2
|
||||||
|
for (ipoint in points) {
|
||||||
|
x[i] = ipoint.getFirst()
|
||||||
|
y[i] = ipoint.getSecond()
|
||||||
|
chpt.append('*')
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
mnplot(x, y, chpt, points.size + 2, width(), length())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun width(): Int {
|
||||||
|
return thePageWidth
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Constructor for MnPlot.
|
||||||
|
*
|
||||||
|
* @param thePageWidth a int.
|
||||||
|
* @param thePageLength a int.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Constructor for MnPlot.
|
||||||
|
*/
|
||||||
|
init {
|
||||||
|
if (thePageWidth > 120) {
|
||||||
|
thePageWidth = 120
|
||||||
|
}
|
||||||
|
if (thePageLength > 56) {
|
||||||
|
thePageLength = 56
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import space.kscience.kmath.optimization.minuit.MINUITPlugin
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
internal object MnPosDef {
|
||||||
|
fun test(st: MinimumState, prec: MnMachinePrecision): MinimumState {
|
||||||
|
val err: MinimumError = test(st.error(), prec)
|
||||||
|
return MinimumState(st.parameters(), err, st.gradient(), st.edm(), st.nfcn())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test(e: MinimumError, prec: MnMachinePrecision): MinimumError {
|
||||||
|
val err: MnAlgebraicSymMatrix = e.invHessian().copy()
|
||||||
|
if (err.size() === 1 && err[0, 0] < prec.eps()) {
|
||||||
|
err[0, 0] = 1.0
|
||||||
|
return MinimumError(err, MnMadePosDef())
|
||||||
|
}
|
||||||
|
if (err.size() === 1 && err[0, 0] > prec.eps()) {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
// std::cout<<"MnPosDef init matrix= "<<err<<std::endl;
|
||||||
|
val epspdf: Double = max(1e-6, prec.eps2())
|
||||||
|
var dgmin: Double = err[0, 0]
|
||||||
|
for (i in 0 until err.nrow()) {
|
||||||
|
if (err[i, i] < prec.eps2()) {
|
||||||
|
MINUITPlugin.logStatic("negative or zero diagonal element $i in covariance matrix")
|
||||||
|
}
|
||||||
|
if (err[i, i] < dgmin) {
|
||||||
|
dgmin = err[i, i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var dg = 0.0
|
||||||
|
if (dgmin < prec.eps2()) {
|
||||||
|
dg = 1.0 + epspdf - dgmin
|
||||||
|
// dg = 0.5*(1. + epspdf - dgmin);
|
||||||
|
MINUITPlugin.logStatic("added $dg to diagonal of error matrix")
|
||||||
|
}
|
||||||
|
val s: RealVector = ArrayRealVector(err.nrow())
|
||||||
|
val p = MnAlgebraicSymMatrix(err.nrow())
|
||||||
|
for (i in 0 until err.nrow()) {
|
||||||
|
err[i, i] = err[i, i] + dg
|
||||||
|
if (err[i, i] < 0.0) {
|
||||||
|
err[i, i] = 1.0
|
||||||
|
}
|
||||||
|
s.setEntry(i, 1.0 / sqrt(err[i, i]))
|
||||||
|
for (j in 0..i) {
|
||||||
|
p[i, j] = err[i, j] * s.getEntry(i) * s.getEntry(j)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// std::cout<<"MnPosDef p: "<<p<<std::endl;
|
||||||
|
val eval: RealVector = p.eigenvalues()
|
||||||
|
val pmin: Double = eval.getEntry(0)
|
||||||
|
var pmax: Double = eval.getEntry(eval.getDimension() - 1)
|
||||||
|
// std::cout<<"pmin= "<<pmin<<" pmax= "<<pmax<<std::endl;
|
||||||
|
pmax = max(abs(pmax), 1.0)
|
||||||
|
if (pmin > epspdf * pmax) {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
val padd = 0.001 * pmax - pmin
|
||||||
|
MINUITPlugin.logStatic("eigenvalues: ")
|
||||||
|
for (i in 0 until err.nrow()) {
|
||||||
|
err[i, i] = err[i, i] * (1.0 + padd)
|
||||||
|
MINUITPlugin.logStatic(java.lang.Double.toString(eval.getEntry(i)))
|
||||||
|
}
|
||||||
|
// std::cout<<"MnPosDef final matrix: "<<err<<std::endl;
|
||||||
|
MINUITPlugin.logStatic("matrix forced pos-def by adding $padd to diagonal")
|
||||||
|
// std::cout<<"eigenvalues: "<<eval<<std::endl;
|
||||||
|
return MinimumError(err, MnMadePosDef())
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,387 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import org.apache.commons.math3.linear.RealVector
|
||||||
|
import ru.inr.mass.minuit.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utilities for printing various minuit results.
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
* @author Darksnake
|
||||||
|
*/
|
||||||
|
object MnPrint {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* print.
|
||||||
|
*
|
||||||
|
* @param os a [PrintWriter] object.
|
||||||
|
* @param vec a [org.apache.commons.math3.linear.RealVector] object.
|
||||||
|
*/
|
||||||
|
fun print(os: PrintWriter, vec: RealVector) {
|
||||||
|
os.println("LAVector parameters:")
|
||||||
|
run {
|
||||||
|
os.println()
|
||||||
|
val nrow: Int = vec.getDimension()
|
||||||
|
for (i in 0 until nrow) {
|
||||||
|
os.printf("%g ", vec.getEntry(i))
|
||||||
|
}
|
||||||
|
os.println()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* print.
|
||||||
|
*
|
||||||
|
* @param os a [PrintWriter] object.
|
||||||
|
* @param matrix a [hep.dataforge.MINUIT.MnAlgebraicSymMatrix] object.
|
||||||
|
*/
|
||||||
|
fun print(os: PrintWriter, matrix: MnAlgebraicSymMatrix) {
|
||||||
|
os.println("LASymMatrix parameters:")
|
||||||
|
run {
|
||||||
|
os.println()
|
||||||
|
val n: Int = matrix.nrow()
|
||||||
|
for (i in 0 until n) {
|
||||||
|
for (j in 0 until n) {
|
||||||
|
os.printf("%10g ", matrix[i, j])
|
||||||
|
}
|
||||||
|
os.println()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* print.
|
||||||
|
*
|
||||||
|
* @param os a [PrintWriter] object.
|
||||||
|
* @param min a [hep.dataforge.MINUIT.FunctionMinimum] object.
|
||||||
|
*/
|
||||||
|
fun print(os: PrintWriter, min: FunctionMinimum) {
|
||||||
|
os.println()
|
||||||
|
if (!min.isValid()) {
|
||||||
|
os.println()
|
||||||
|
os.println("WARNING: Minuit did not converge.")
|
||||||
|
os.println()
|
||||||
|
} else {
|
||||||
|
os.println()
|
||||||
|
os.println("Minuit did successfully converge.")
|
||||||
|
os.println()
|
||||||
|
}
|
||||||
|
os.printf("# of function calls: %d\n", min.nfcn())
|
||||||
|
os.printf("minimum function value: %g\n", min.fval())
|
||||||
|
os.printf("minimum edm: %g\n", min.edm())
|
||||||
|
os.println("minimum internal state vector: " + min.parameters().vec())
|
||||||
|
if (min.hasValidCovariance()) {
|
||||||
|
os.println("minimum internal covariance matrix: " + min.error().matrix())
|
||||||
|
}
|
||||||
|
os.println(min.userParameters())
|
||||||
|
os.println(min.userCovariance())
|
||||||
|
os.println(min.userState().globalCC())
|
||||||
|
if (!min.isValid()) {
|
||||||
|
os.println("WARNING: FunctionMinimum is invalid.")
|
||||||
|
}
|
||||||
|
os.println()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* print.
|
||||||
|
*
|
||||||
|
* @param os a [PrintWriter] object.
|
||||||
|
* @param min a [hep.dataforge.MINUIT.MinimumState] object.
|
||||||
|
*/
|
||||||
|
fun print(os: PrintWriter, min: MinimumState) {
|
||||||
|
os.println()
|
||||||
|
os.printf("minimum function value: %g\n", min.fval())
|
||||||
|
os.printf("minimum edm: %g\n", min.edm())
|
||||||
|
os.println("minimum internal state vector: " + min.vec())
|
||||||
|
os.println("minimum internal gradient vector: " + min.gradient().getGradient())
|
||||||
|
if (min.hasCovariance()) {
|
||||||
|
os.println("minimum internal covariance matrix: " + min.error().matrix())
|
||||||
|
}
|
||||||
|
os.println()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* print.
|
||||||
|
*
|
||||||
|
* @param os a [PrintWriter] object.
|
||||||
|
* @param par a [hep.dataforge.MINUIT.MnUserParameters] object.
|
||||||
|
*/
|
||||||
|
fun print(os: PrintWriter, par: MnUserParameters) {
|
||||||
|
os.println()
|
||||||
|
os.println("# ext. |" + "| name |" + "| type |" + "| value |" + "| error +/- ")
|
||||||
|
os.println()
|
||||||
|
var atLoLim = false
|
||||||
|
var atHiLim = false
|
||||||
|
for (ipar in par.parameters()) {
|
||||||
|
os.printf(" %5d || %9s || ", ipar.number(), ipar.name())
|
||||||
|
if (ipar.isConst()) {
|
||||||
|
os.printf(" || %10g ||", ipar.value())
|
||||||
|
} else if (ipar.isFixed()) {
|
||||||
|
os.printf(" fixed || %10g ||\n", ipar.value())
|
||||||
|
} else if (ipar.hasLimits()) {
|
||||||
|
if (ipar.error() > 0.0) {
|
||||||
|
os.printf(" limited || %10g", ipar.value())
|
||||||
|
if (abs(ipar.value() - ipar.lowerLimit()) < par.precision().eps2()) {
|
||||||
|
os.print("* ")
|
||||||
|
atLoLim = true
|
||||||
|
}
|
||||||
|
if (abs(ipar.value() - ipar.upperLimit()) < par.precision().eps2()) {
|
||||||
|
os.print("**")
|
||||||
|
atHiLim = true
|
||||||
|
}
|
||||||
|
os.printf(" || %10g\n", ipar.error())
|
||||||
|
} else {
|
||||||
|
os.printf(" free || %10g || no\n", ipar.value())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (ipar.error() > 0.0) {
|
||||||
|
os.printf(" free || %10g || %10g\n", ipar.value(), ipar.error())
|
||||||
|
} else {
|
||||||
|
os.printf(" free || %10g || no\n", ipar.value())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
os.println()
|
||||||
|
if (atLoLim) {
|
||||||
|
os.print("* parameter is at lower limit")
|
||||||
|
}
|
||||||
|
if (atHiLim) {
|
||||||
|
os.print("** parameter is at upper limit")
|
||||||
|
}
|
||||||
|
os.println()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* print.
|
||||||
|
*
|
||||||
|
* @param os a [PrintWriter] object.
|
||||||
|
* @param matrix a [hep.dataforge.MINUIT.MnUserCovariance] object.
|
||||||
|
*/
|
||||||
|
fun print(os: PrintWriter, matrix: MnUserCovariance) {
|
||||||
|
os.println()
|
||||||
|
os.println("MnUserCovariance: ")
|
||||||
|
run {
|
||||||
|
os.println()
|
||||||
|
val n: Int = matrix.nrow()
|
||||||
|
for (i in 0 until n) {
|
||||||
|
for (j in 0 until n) {
|
||||||
|
os.printf("%10g ", matrix[i, j])
|
||||||
|
}
|
||||||
|
os.println()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
os.println()
|
||||||
|
os.println("MnUserCovariance parameter correlations: ")
|
||||||
|
run {
|
||||||
|
os.println()
|
||||||
|
val n: Int = matrix.nrow()
|
||||||
|
for (i in 0 until n) {
|
||||||
|
val di: Double = matrix[i, i]
|
||||||
|
for (j in 0 until n) {
|
||||||
|
val dj: Double = matrix[j, j]
|
||||||
|
os.printf("%g ", matrix[i, j] / sqrt(abs(di * dj)))
|
||||||
|
}
|
||||||
|
os.println()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* print.
|
||||||
|
*
|
||||||
|
* @param os a [PrintWriter] object.
|
||||||
|
* @param coeff a [hep.dataforge.MINUIT.MnGlobalCorrelationCoeff] object.
|
||||||
|
*/
|
||||||
|
fun print(os: PrintWriter, coeff: MnGlobalCorrelationCoeff) {
|
||||||
|
os.println()
|
||||||
|
os.println("MnGlobalCorrelationCoeff: ")
|
||||||
|
run {
|
||||||
|
os.println()
|
||||||
|
for (i in 0 until coeff.globalCC().length) {
|
||||||
|
os.printf("%g\n", coeff.globalCC()[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* print.
|
||||||
|
*
|
||||||
|
* @param os a [PrintWriter] object.
|
||||||
|
* @param state a [hep.dataforge.MINUIT.MnUserParameterState] object.
|
||||||
|
*/
|
||||||
|
fun print(os: PrintWriter, state: MnUserParameterState) {
|
||||||
|
os.println()
|
||||||
|
if (!state.isValid()) {
|
||||||
|
os.println()
|
||||||
|
os.println("WARNING: MnUserParameterState is not valid.")
|
||||||
|
os.println()
|
||||||
|
}
|
||||||
|
os.println("# of function calls: " + state.nfcn())
|
||||||
|
os.println("function value: " + state.fval())
|
||||||
|
os.println("expected distance to the minimum (edm): " + state.edm())
|
||||||
|
os.println("external parameters: " + state.parameters())
|
||||||
|
if (state.hasCovariance()) {
|
||||||
|
os.println("covariance matrix: " + state.covariance())
|
||||||
|
}
|
||||||
|
if (state.hasGlobalCC()) {
|
||||||
|
os.println("global correlation coefficients : " + state.globalCC())
|
||||||
|
}
|
||||||
|
if (!state.isValid()) {
|
||||||
|
os.println("WARNING: MnUserParameterState is not valid.")
|
||||||
|
}
|
||||||
|
os.println()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* print.
|
||||||
|
*
|
||||||
|
* @param os a [PrintWriter] object.
|
||||||
|
* @param me a [hep.dataforge.MINUIT.MinosError] object.
|
||||||
|
*/
|
||||||
|
fun print(os: PrintWriter, me: MinosError) {
|
||||||
|
os.println()
|
||||||
|
os.printf("Minos # of function calls: %d\n", me.nfcn())
|
||||||
|
if (!me.isValid()) {
|
||||||
|
os.println("Minos error is not valid.")
|
||||||
|
}
|
||||||
|
if (!me.lowerValid()) {
|
||||||
|
os.println("lower Minos error is not valid.")
|
||||||
|
}
|
||||||
|
if (!me.upperValid()) {
|
||||||
|
os.println("upper Minos error is not valid.")
|
||||||
|
}
|
||||||
|
if (me.atLowerLimit()) {
|
||||||
|
os.println("Minos error is lower limit of parameter " + me.parameter())
|
||||||
|
}
|
||||||
|
if (me.atUpperLimit()) {
|
||||||
|
os.println("Minos error is upper limit of parameter " + me.parameter())
|
||||||
|
}
|
||||||
|
if (me.atLowerMaxFcn()) {
|
||||||
|
os.println("Minos number of function calls for lower error exhausted.")
|
||||||
|
}
|
||||||
|
if (me.atUpperMaxFcn()) {
|
||||||
|
os.println("Minos number of function calls for upper error exhausted.")
|
||||||
|
}
|
||||||
|
if (me.lowerNewMin()) {
|
||||||
|
os.println("Minos found a new minimum in negative direction.")
|
||||||
|
os.println(me.lowerState())
|
||||||
|
}
|
||||||
|
if (me.upperNewMin()) {
|
||||||
|
os.println("Minos found a new minimum in positive direction.")
|
||||||
|
os.println(me.upperState())
|
||||||
|
}
|
||||||
|
os.println("# ext. || name || value@min || negative || positive ")
|
||||||
|
os.printf("%4d||%10s||%10g||%10g||%10g\n",
|
||||||
|
me.parameter(),
|
||||||
|
me.lowerState().name(me.parameter()),
|
||||||
|
me.min(),
|
||||||
|
me.lower(),
|
||||||
|
me.upper())
|
||||||
|
os.println()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* print.
|
||||||
|
*
|
||||||
|
* @param os a [PrintWriter] object.
|
||||||
|
* @param ce a [hep.dataforge.MINUIT.ContoursError] object.
|
||||||
|
*/
|
||||||
|
fun print(os: PrintWriter, ce: ContoursError) {
|
||||||
|
os.println()
|
||||||
|
os.println("Contours # of function calls: " + ce.nfcn())
|
||||||
|
os.println("MinosError in x: ")
|
||||||
|
os.println(ce.xMinosError())
|
||||||
|
os.println("MinosError in y: ")
|
||||||
|
os.println(ce.yMinosError())
|
||||||
|
val plot = MnPlot()
|
||||||
|
plot.plot(ce.xmin(), ce.ymin(), ce.points())
|
||||||
|
for ((i, ipoint) in ce.points().withIndex()) {
|
||||||
|
os.printf("%d %10g %10g\n", i, ipoint.getFirst(), ipoint.getSecond())
|
||||||
|
}
|
||||||
|
os.println()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toString(x: RealVector): String {
|
||||||
|
val writer: java.io.StringWriter = java.io.StringWriter()
|
||||||
|
PrintWriter(writer).use { pw -> print(pw, x) }
|
||||||
|
return writer.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toString(x: MnAlgebraicSymMatrix?): String {
|
||||||
|
val writer: java.io.StringWriter = java.io.StringWriter()
|
||||||
|
PrintWriter(writer).use { pw -> print(pw, x) }
|
||||||
|
return writer.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toString(min: FunctionMinimum?): String {
|
||||||
|
val writer: java.io.StringWriter = java.io.StringWriter()
|
||||||
|
PrintWriter(writer).use { pw -> print(pw, min) }
|
||||||
|
return writer.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toString(x: MinimumState?): String {
|
||||||
|
val writer: java.io.StringWriter = java.io.StringWriter()
|
||||||
|
PrintWriter(writer).use { pw -> print(pw, x) }
|
||||||
|
return writer.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toString(x: MnUserParameters?): String {
|
||||||
|
val writer: java.io.StringWriter = java.io.StringWriter()
|
||||||
|
PrintWriter(writer).use { pw -> print(pw, x) }
|
||||||
|
return writer.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toString(x: MnUserCovariance?): String {
|
||||||
|
val writer: java.io.StringWriter = java.io.StringWriter()
|
||||||
|
PrintWriter(writer).use { pw -> print(pw, x) }
|
||||||
|
return writer.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toString(x: MnGlobalCorrelationCoeff?): String {
|
||||||
|
val writer: java.io.StringWriter = java.io.StringWriter()
|
||||||
|
PrintWriter(writer).use { pw -> print(pw, x) }
|
||||||
|
return writer.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toString(x: MnUserParameterState?): String {
|
||||||
|
val writer: java.io.StringWriter = java.io.StringWriter()
|
||||||
|
PrintWriter(writer).use { pw -> print(pw, x) }
|
||||||
|
return writer.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toString(x: MinosError?): String {
|
||||||
|
val writer: java.io.StringWriter = java.io.StringWriter()
|
||||||
|
PrintWriter(writer).use { pw -> print(pw, x) }
|
||||||
|
return writer.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toString(x: ContoursError?): String {
|
||||||
|
val writer: java.io.StringWriter = java.io.StringWriter()
|
||||||
|
PrintWriter(writer).use { pw -> print(pw, x) }
|
||||||
|
return writer.toString()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,181 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import ru.inr.mass.maths.MultiFunction
|
||||||
|
import ru.inr.mass.minuit.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MnScan scans the value of the user function by varying one parameter. It is
|
||||||
|
* sometimes useful for debugging the user function or finding a reasonable
|
||||||
|
* starting point.
|
||||||
|
* construct from MultiFunction + MnUserParameterState + MnStrategy
|
||||||
|
*
|
||||||
|
* @param str a [hep.dataforge.MINUIT.MnStrategy] object.
|
||||||
|
* @param par a [hep.dataforge.MINUIT.MnUserParameterState] object.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
* @version $Id$
|
||||||
|
* @author Darksnake
|
||||||
|
*/
|
||||||
|
class MnScan(fcn: MultiFunction?, par: MnUserParameterState, str: MnStrategy) : MnApplication(fcn, par, str) {
|
||||||
|
private val theMinimizer: ScanMinimizer = ScanMinimizer()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from MultiFunction + double[] for parameters and errors
|
||||||
|
* with default strategy
|
||||||
|
*
|
||||||
|
* @param err an array of double.
|
||||||
|
* @param par an array of double.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
*/
|
||||||
|
constructor(fcn: MultiFunction?, par: DoubleArray, err: DoubleArray) : this(fcn, par, err, DEFAULT_STRATEGY)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from MultiFunction + double[] for parameters and errors
|
||||||
|
*
|
||||||
|
* @param stra a int.
|
||||||
|
* @param err an array of double.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
* @param par an array of double.
|
||||||
|
*/
|
||||||
|
constructor(fcn: MultiFunction?, par: DoubleArray, err: DoubleArray, stra: Int) : this(fcn,
|
||||||
|
MnUserParameterState(par, err),
|
||||||
|
MnStrategy(stra))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from MultiFunction + double[] for parameters and
|
||||||
|
* MnUserCovariance with default strategy
|
||||||
|
*
|
||||||
|
* @param cov a [hep.dataforge.MINUIT.MnUserCovariance] object.
|
||||||
|
* @param par an array of double.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
*/
|
||||||
|
constructor(fcn: MultiFunction?, par: DoubleArray, cov: MnUserCovariance) : this(fcn, par, cov, DEFAULT_STRATEGY)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from MultiFunction + double[] for parameters and
|
||||||
|
* MnUserCovariance
|
||||||
|
*
|
||||||
|
* @param stra a int.
|
||||||
|
* @param cov a [hep.dataforge.MINUIT.MnUserCovariance] object.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
* @param par an array of double.
|
||||||
|
*/
|
||||||
|
constructor(fcn: MultiFunction?, par: DoubleArray, cov: MnUserCovariance, stra: Int) : this(fcn,
|
||||||
|
MnUserParameterState(par, cov),
|
||||||
|
MnStrategy(stra))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from MultiFunction + MnUserParameters with default
|
||||||
|
* strategy
|
||||||
|
*
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
* @param par a [hep.dataforge.MINUIT.MnUserParameters] object.
|
||||||
|
*/
|
||||||
|
constructor(fcn: MultiFunction?, par: MnUserParameters) : this(fcn, par, DEFAULT_STRATEGY)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from MultiFunction + MnUserParameters
|
||||||
|
*
|
||||||
|
* @param stra a int.
|
||||||
|
* @param par a [hep.dataforge.MINUIT.MnUserParameters] object.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
*/
|
||||||
|
constructor(fcn: MultiFunction?, par: MnUserParameters, stra: Int) : this(fcn,
|
||||||
|
MnUserParameterState(par),
|
||||||
|
MnStrategy(stra))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from MultiFunction + MnUserParameters + MnUserCovariance
|
||||||
|
* with default strategy
|
||||||
|
*
|
||||||
|
* @param cov a [hep.dataforge.MINUIT.MnUserCovariance] object.
|
||||||
|
* @param par a [hep.dataforge.MINUIT.MnUserParameters] object.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
*/
|
||||||
|
constructor(fcn: MultiFunction?, par: MnUserParameters, cov: MnUserCovariance) : this(fcn,
|
||||||
|
par,
|
||||||
|
cov,
|
||||||
|
DEFAULT_STRATEGY)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from MultiFunction + MnUserParameters + MnUserCovariance
|
||||||
|
*
|
||||||
|
* @param stra a int.
|
||||||
|
* @param cov a [hep.dataforge.MINUIT.MnUserCovariance] object.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
* @param par a [hep.dataforge.MINUIT.MnUserParameters] object.
|
||||||
|
*/
|
||||||
|
constructor(fcn: MultiFunction?, par: MnUserParameters, cov: MnUserCovariance, stra: Int) : this(fcn,
|
||||||
|
MnUserParameterState(par, cov),
|
||||||
|
MnStrategy(stra))
|
||||||
|
|
||||||
|
override fun minimizer(): ModularFunctionMinimizer {
|
||||||
|
return theMinimizer
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* scan.
|
||||||
|
*
|
||||||
|
* @param par a int.
|
||||||
|
* @return a [List] object.
|
||||||
|
*/
|
||||||
|
fun scan(par: Int): List<Range> {
|
||||||
|
return scan(par, 41)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* scan.
|
||||||
|
*
|
||||||
|
* @param par a int.
|
||||||
|
* @param maxsteps a int.
|
||||||
|
* @return a [List] object.
|
||||||
|
*/
|
||||||
|
fun scan(par: Int, maxsteps: Int): List<Range> {
|
||||||
|
return scan(par, maxsteps, 0.0, 0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scans the value of the user function by varying parameter number par,
|
||||||
|
* leaving all other parameters fixed at the current value. If par is not
|
||||||
|
* specified, all variable parameters are scanned in sequence. The number of
|
||||||
|
* points npoints in the scan is 40 by default, and cannot exceed 100. The
|
||||||
|
* range of the scan is by default 2 standard deviations on each side of the
|
||||||
|
* current best value, but can be specified as from low to high. After each
|
||||||
|
* scan, if a new minimum is found, the best parameter values are retained
|
||||||
|
* as start values for future scans or minimizations. The curve resulting
|
||||||
|
* from each scan can be plotted on the output terminal using MnPlot in
|
||||||
|
* order to show the approximate behaviour of the function.
|
||||||
|
*
|
||||||
|
* @param high a double.
|
||||||
|
* @param par a int.
|
||||||
|
* @param maxsteps a int.
|
||||||
|
* @param low a double.
|
||||||
|
* @return a [List] object.
|
||||||
|
*/
|
||||||
|
fun scan(par: Int, maxsteps: Int, low: Double, high: Double): List<Range> {
|
||||||
|
val scan = MnParameterScan(theFCN, theState.parameters())
|
||||||
|
var amin: Double = scan.fval()
|
||||||
|
val result: List<Range> = scan.scan(par, maxsteps, low, high)
|
||||||
|
if (scan.fval() < amin) {
|
||||||
|
theState.setValue(par, scan.parameters().value(par))
|
||||||
|
amin = scan.fval()
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import space.kscience.kmath.optimization.minuit.MINUITPlugin
|
||||||
|
import ru.inr.mass.minuit.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
internal class MnSeedGenerator : MinimumSeedGenerator {
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
fun generate(fcn: MnFcn, gc: GradientCalculator, st: MnUserParameterState, stra: MnStrategy): MinimumSeed {
|
||||||
|
val n: Int = st.variableParameters()
|
||||||
|
val prec: MnMachinePrecision = st.precision()
|
||||||
|
|
||||||
|
// initial starting values
|
||||||
|
val x: RealVector = ArrayRealVector(n)
|
||||||
|
for (i in 0 until n) {
|
||||||
|
x.setEntry(i, st.intParameters()[i])
|
||||||
|
}
|
||||||
|
val fcnmin: Double = fcn.value(x)
|
||||||
|
val pa = MinimumParameters(x, fcnmin)
|
||||||
|
val dgrad: FunctionGradient
|
||||||
|
if (gc is AnalyticalGradientCalculator) {
|
||||||
|
val igc = InitialGradientCalculator(fcn, st.getTransformation(), stra)
|
||||||
|
val tmp: FunctionGradient = igc.gradient(pa)
|
||||||
|
val grd: FunctionGradient = gc.gradient(pa)
|
||||||
|
dgrad = FunctionGradient(grd.getGradient(), tmp.getGradientDerivative(), tmp.getStep())
|
||||||
|
if (gc.checkGradient()) {
|
||||||
|
val good = true
|
||||||
|
val hgc = HessianGradientCalculator(fcn, st.getTransformation(), MnStrategy(2))
|
||||||
|
val hgrd: Pair<FunctionGradient, RealVector> = hgc.deltaGradient(pa, dgrad)
|
||||||
|
for (i in 0 until n) {
|
||||||
|
val provided: Double = grd.getGradient().getEntry(i)
|
||||||
|
val calculated: Double = hgrd.getFirst().getGradient().getEntry(i)
|
||||||
|
val delta: Double = hgrd.getSecond().getEntry(i)
|
||||||
|
if (abs(calculated - provided) > delta) {
|
||||||
|
MINUITPlugin.logStatic(""
|
||||||
|
+ "gradient discrepancy of external parameter \"%d\" "
|
||||||
|
+ "(internal parameter \"%d\") too large. Expected: \"%f\", provided: \"%f\"",
|
||||||
|
st.getTransformation().extOfInt(i), i, provided, calculated)
|
||||||
|
|
||||||
|
//
|
||||||
|
// MINUITPlugin.logStatic("gradient discrepancy of external parameter "
|
||||||
|
// + st.getTransformation().extOfInt(i)
|
||||||
|
// + " (internal parameter " + i + ") too large.");
|
||||||
|
// good = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!good) {
|
||||||
|
MINUITPlugin.logStatic("Minuit does not accept user specified gradient.")
|
||||||
|
// assert(good);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dgrad = gc.gradient(pa)
|
||||||
|
}
|
||||||
|
val mat = MnAlgebraicSymMatrix(n)
|
||||||
|
var dcovar = 1.0
|
||||||
|
if (st.hasCovariance()) {
|
||||||
|
for (i in 0 until n) {
|
||||||
|
for (j in i until n) {
|
||||||
|
mat[i, j] = st.intCovariance()[i, j]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dcovar = 0.0
|
||||||
|
} else {
|
||||||
|
for (i in 0 until n) {
|
||||||
|
mat[i, i] = if (abs(dgrad.getGradientDerivative()
|
||||||
|
.getEntry(i)) > prec.eps2()
|
||||||
|
) 1.0 / dgrad.getGradientDerivative().getEntry(i) else 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val err = MinimumError(mat, dcovar)
|
||||||
|
val edm: Double = VariableMetricEDMEstimator().estimate(dgrad, err)
|
||||||
|
var state = MinimumState(pa, err, dgrad, edm, fcn.numOfCalls())
|
||||||
|
if (NegativeG2LineSearch.hasNegativeG2(dgrad, prec)) {
|
||||||
|
state = if (gc is AnalyticalGradientCalculator) {
|
||||||
|
val ngc = Numerical2PGradientCalculator(fcn, st.getTransformation(), stra)
|
||||||
|
NegativeG2LineSearch.search(fcn, state, ngc, prec)
|
||||||
|
} else {
|
||||||
|
NegativeG2LineSearch.search(fcn, state, gc, prec)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (stra.strategy() === 2 && !st.hasCovariance()) {
|
||||||
|
//calculate full 2nd derivative
|
||||||
|
val tmp: MinimumState = MnHesse(stra).calculate(fcn, state, st.getTransformation(), 0)
|
||||||
|
return MinimumSeed(tmp, st.getTransformation())
|
||||||
|
}
|
||||||
|
return MinimumSeed(state, st.getTransformation())
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,138 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import ru.inr.mass.maths.MultiFunction
|
||||||
|
import ru.inr.mass.minuit.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SIMPLEX is a function minimization method using the simplex method of Nelder
|
||||||
|
* and Mead. MnSimplex provides minimization of the function by the method of
|
||||||
|
* SIMPLEX and the functionality for parameters interaction. It also retains the
|
||||||
|
* result from the last minimization in case the user may want to do subsequent
|
||||||
|
* minimization steps with parameter interactions in between the minimization
|
||||||
|
* requests. As SIMPLEX is a stepping method it does not produce a covariance
|
||||||
|
* matrix.
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
* @author Darksnake
|
||||||
|
*/
|
||||||
|
class MnSimplex
|
||||||
|
/**
|
||||||
|
* construct from MultiFunction + MnUserParameterState + MnStrategy
|
||||||
|
*
|
||||||
|
* @param str a [hep.dataforge.MINUIT.MnStrategy] object.
|
||||||
|
* @param par a [hep.dataforge.MINUIT.MnUserParameterState] object.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
*/
|
||||||
|
(fcn: MultiFunction?, par: MnUserParameterState, str: MnStrategy) : MnApplication(fcn, par, str) {
|
||||||
|
private val theMinimizer: SimplexMinimizer = SimplexMinimizer()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from MultiFunction + double[] for parameters and errors
|
||||||
|
* with default strategy
|
||||||
|
*
|
||||||
|
* @param err an array of double.
|
||||||
|
* @param par an array of double.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
*/
|
||||||
|
constructor(fcn: MultiFunction?, par: DoubleArray, err: DoubleArray) : this(fcn, par, err, DEFAULT_STRATEGY)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from MultiFunction + double[] for parameters and errors
|
||||||
|
*
|
||||||
|
* @param stra a int.
|
||||||
|
* @param err an array of double.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
* @param par an array of double.
|
||||||
|
*/
|
||||||
|
constructor(fcn: MultiFunction?, par: DoubleArray, err: DoubleArray, stra: Int) : this(fcn,
|
||||||
|
MnUserParameterState(par, err),
|
||||||
|
MnStrategy(stra))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from MultiFunction + double[] for parameters and
|
||||||
|
* MnUserCovariance with default strategy
|
||||||
|
*
|
||||||
|
* @param cov a [hep.dataforge.MINUIT.MnUserCovariance] object.
|
||||||
|
* @param par an array of double.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
*/
|
||||||
|
constructor(fcn: MultiFunction?, par: DoubleArray, cov: MnUserCovariance) : this(fcn, par, cov, DEFAULT_STRATEGY)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from MultiFunction + double[] for parameters and
|
||||||
|
* MnUserCovariance
|
||||||
|
*
|
||||||
|
* @param stra a int.
|
||||||
|
* @param cov a [hep.dataforge.MINUIT.MnUserCovariance] object.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
* @param par an array of double.
|
||||||
|
*/
|
||||||
|
constructor(fcn: MultiFunction?, par: DoubleArray, cov: MnUserCovariance, stra: Int) : this(fcn,
|
||||||
|
MnUserParameterState(par, cov),
|
||||||
|
MnStrategy(stra))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from MultiFunction + MnUserParameters with default
|
||||||
|
* strategy
|
||||||
|
*
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
* @param par a [hep.dataforge.MINUIT.MnUserParameters] object.
|
||||||
|
*/
|
||||||
|
constructor(fcn: MultiFunction?, par: MnUserParameters) : this(fcn, par, DEFAULT_STRATEGY)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from MultiFunction + MnUserParameters
|
||||||
|
*
|
||||||
|
* @param stra a int.
|
||||||
|
* @param par a [hep.dataforge.MINUIT.MnUserParameters] object.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
*/
|
||||||
|
constructor(fcn: MultiFunction?, par: MnUserParameters, stra: Int) : this(fcn,
|
||||||
|
MnUserParameterState(par),
|
||||||
|
MnStrategy(stra))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from MultiFunction + MnUserParameters + MnUserCovariance
|
||||||
|
* with default strategy
|
||||||
|
*
|
||||||
|
* @param cov a [hep.dataforge.MINUIT.MnUserCovariance] object.
|
||||||
|
* @param par a [hep.dataforge.MINUIT.MnUserParameters] object.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
*/
|
||||||
|
constructor(fcn: MultiFunction?, par: MnUserParameters, cov: MnUserCovariance) : this(fcn,
|
||||||
|
par,
|
||||||
|
cov,
|
||||||
|
DEFAULT_STRATEGY)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from MultiFunction + MnUserParameters + MnUserCovariance
|
||||||
|
*
|
||||||
|
* @param stra a int.
|
||||||
|
* @param cov a [hep.dataforge.MINUIT.MnUserCovariance] object.
|
||||||
|
* @param fcn a [MultiFunction] object.
|
||||||
|
* @param par a [hep.dataforge.MINUIT.MnUserParameters] object.
|
||||||
|
*/
|
||||||
|
constructor(fcn: MultiFunction?, par: MnUserParameters, cov: MnUserCovariance, stra: Int) : this(fcn,
|
||||||
|
MnUserParameterState(par, cov),
|
||||||
|
MnStrategy(stra))
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
override fun minimizer(): ModularFunctionMinimizer {
|
||||||
|
return theMinimizer
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,310 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API class for defining three levels of strategies: low (0), medium (1), high
|
||||||
|
* (2).
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* At many places in the analysis of the FCN (the user provided function),
|
||||||
|
* MINUIT must decide whether to be <I>safe</I> and waste a few function calls
|
||||||
|
* in order to know where it is, or to be <I>fast</I> and attempt to get the
|
||||||
|
* requested results with the fewest possible calls at a certain risk of not
|
||||||
|
* obtaining the precision desired by the user. In order to allow the user to
|
||||||
|
* infuence these decisions, the MnStrategy class allows the user to control
|
||||||
|
* different settings. MnStrategy can be instantiated with three different
|
||||||
|
* minimization quality levels for low (0), medium (1) and high (2) quality.
|
||||||
|
* Default settings for iteration cycles and tolerances are initialized then.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* The default setting is set for medium quality. Value 0 (low) indicates to
|
||||||
|
* MINUIT that it should economize function calls; it is intended for cases
|
||||||
|
* where there are many variable parameters and/or the function takes a long
|
||||||
|
* time to calculate and/or the user is not interested in very precise values
|
||||||
|
* for parameter errors. On the other hand, value 2 (high) indicates that MINUIT
|
||||||
|
* is allowed to waste function calls in order to be sure that all values are
|
||||||
|
* precise; it is it is intended for cases where the function is evaluated in a
|
||||||
|
* relatively short time and/or where the parameter errors must be calculated
|
||||||
|
* reliably.
|
||||||
|
*
|
||||||
|
* In addition all constants set in MnStrategy can be changed individually by
|
||||||
|
* the user, e.g. the number of iteration cycles in the numerical gradient.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Acts on: Migrad (behavioural), Minos (lowers strategy by 1 for Minos-own
|
||||||
|
* minimization), Hesse (iterations), Numerical2PDerivative (iterations)
|
||||||
|
*
|
||||||
|
* @author Darksnake
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
class MnStrategy {
|
||||||
|
private var theGradNCyc = 0
|
||||||
|
private var theGradTlr = 0.0
|
||||||
|
private var theGradTlrStp = 0.0
|
||||||
|
private var theHessGradNCyc = 0
|
||||||
|
|
||||||
|
//default strategy
|
||||||
|
private var theHessNCyc = 0
|
||||||
|
private var theHessTlrG2 = 0.0
|
||||||
|
private var theHessTlrStp = 0.0
|
||||||
|
private var theStrategy = 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a MnStrategy object with the default strategy (medium)
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
setMediumStrategy()
|
||||||
|
}
|
||||||
|
//user defined strategy (0, 1, >=2)
|
||||||
|
/**
|
||||||
|
* Creates a MnStrategy object with the user specified strategy.
|
||||||
|
*
|
||||||
|
* @param stra The use defined strategy, 0=low, 1 medium, 2=high.
|
||||||
|
*/
|
||||||
|
constructor(stra: Int) {
|
||||||
|
if (stra == 0) {
|
||||||
|
setLowStrategy()
|
||||||
|
} else if (stra == 1) {
|
||||||
|
setMediumStrategy()
|
||||||
|
} else {
|
||||||
|
setHighStrategy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* gradientNCycles.
|
||||||
|
*
|
||||||
|
* @return a int.
|
||||||
|
*/
|
||||||
|
fun gradientNCycles(): Int {
|
||||||
|
return theGradNCyc
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* gradientStepTolerance.
|
||||||
|
*
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
fun gradientStepTolerance(): Double {
|
||||||
|
return theGradTlrStp
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* gradientTolerance.
|
||||||
|
*
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
fun gradientTolerance(): Double {
|
||||||
|
return theGradTlr
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* hessianG2Tolerance.
|
||||||
|
*
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
fun hessianG2Tolerance(): Double {
|
||||||
|
return theHessTlrG2
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* hessianGradientNCycles.
|
||||||
|
*
|
||||||
|
* @return a int.
|
||||||
|
*/
|
||||||
|
fun hessianGradientNCycles(): Int {
|
||||||
|
return theHessGradNCyc
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* hessianNCycles.
|
||||||
|
*
|
||||||
|
* @return a int.
|
||||||
|
*/
|
||||||
|
fun hessianNCycles(): Int {
|
||||||
|
return theHessNCyc
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* hessianStepTolerance.
|
||||||
|
*
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
fun hessianStepTolerance(): Double {
|
||||||
|
return theHessTlrStp
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* isHigh.
|
||||||
|
*
|
||||||
|
* @return a boolean.
|
||||||
|
*/
|
||||||
|
fun isHigh(): Boolean {
|
||||||
|
return theStrategy >= 2
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* isLow.
|
||||||
|
*
|
||||||
|
* @return a boolean.
|
||||||
|
*/
|
||||||
|
fun isLow(): Boolean {
|
||||||
|
return theStrategy <= 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* isMedium.
|
||||||
|
*
|
||||||
|
* @return a boolean.
|
||||||
|
*/
|
||||||
|
fun isMedium(): Boolean {
|
||||||
|
return theStrategy == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setGradientNCycles.
|
||||||
|
*
|
||||||
|
* @param n a int.
|
||||||
|
*/
|
||||||
|
fun setGradientNCycles(n: Int) {
|
||||||
|
theGradNCyc = n
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setGradientStepTolerance.
|
||||||
|
*
|
||||||
|
* @param stp a double.
|
||||||
|
*/
|
||||||
|
fun setGradientStepTolerance(stp: Double) {
|
||||||
|
theGradTlrStp = stp
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setGradientTolerance.
|
||||||
|
*
|
||||||
|
* @param toler a double.
|
||||||
|
*/
|
||||||
|
fun setGradientTolerance(toler: Double) {
|
||||||
|
theGradTlr = toler
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setHessianG2Tolerance.
|
||||||
|
*
|
||||||
|
* @param toler a double.
|
||||||
|
*/
|
||||||
|
fun setHessianG2Tolerance(toler: Double) {
|
||||||
|
theHessTlrG2 = toler
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setHessianGradientNCycles.
|
||||||
|
*
|
||||||
|
* @param n a int.
|
||||||
|
*/
|
||||||
|
fun setHessianGradientNCycles(n: Int) {
|
||||||
|
theHessGradNCyc = n
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setHessianNCycles.
|
||||||
|
*
|
||||||
|
* @param n a int.
|
||||||
|
*/
|
||||||
|
fun setHessianNCycles(n: Int) {
|
||||||
|
theHessNCyc = n
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setHessianStepTolerance.
|
||||||
|
*
|
||||||
|
* @param stp a double.
|
||||||
|
*/
|
||||||
|
fun setHessianStepTolerance(stp: Double) {
|
||||||
|
theHessTlrStp = stp
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setHighStrategy() {
|
||||||
|
theStrategy = 2
|
||||||
|
setGradientNCycles(5)
|
||||||
|
setGradientStepTolerance(0.1)
|
||||||
|
setGradientTolerance(0.02)
|
||||||
|
setHessianNCycles(7)
|
||||||
|
setHessianStepTolerance(0.1)
|
||||||
|
setHessianG2Tolerance(0.02)
|
||||||
|
setHessianGradientNCycles(6)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setLowStrategy.
|
||||||
|
*/
|
||||||
|
fun setLowStrategy() {
|
||||||
|
theStrategy = 0
|
||||||
|
setGradientNCycles(2)
|
||||||
|
setGradientStepTolerance(0.5)
|
||||||
|
setGradientTolerance(0.1)
|
||||||
|
setHessianNCycles(3)
|
||||||
|
setHessianStepTolerance(0.5)
|
||||||
|
setHessianG2Tolerance(0.1)
|
||||||
|
setHessianGradientNCycles(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setMediumStrategy.
|
||||||
|
*/
|
||||||
|
fun setMediumStrategy() {
|
||||||
|
theStrategy = 1
|
||||||
|
setGradientNCycles(3)
|
||||||
|
setGradientStepTolerance(0.3)
|
||||||
|
setGradientTolerance(0.05)
|
||||||
|
setHessianNCycles(5)
|
||||||
|
setHessianStepTolerance(0.3)
|
||||||
|
setHessianG2Tolerance(0.05)
|
||||||
|
setHessianGradientNCycles(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* strategy.
|
||||||
|
*
|
||||||
|
* @return a int.
|
||||||
|
*/
|
||||||
|
fun strategy(): Int {
|
||||||
|
return theStrategy
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,147 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MnUserCovariance is the external covariance matrix designed for the
|
||||||
|
* interaction of the user. The result of the minimization (internal covariance
|
||||||
|
* matrix) is converted into the user representable format. It can also be used
|
||||||
|
* as input prior to the minimization. The size of the covariance matrix is
|
||||||
|
* according to the number of variable parameters (free and limited).
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
* @author Darksnake
|
||||||
|
*/
|
||||||
|
class MnUserCovariance {
|
||||||
|
private var theData: DoubleArray
|
||||||
|
private var theNRow: Int
|
||||||
|
|
||||||
|
private constructor(other: MnUserCovariance) {
|
||||||
|
theData = other.theData.clone()
|
||||||
|
theNRow = other.theNRow
|
||||||
|
}
|
||||||
|
|
||||||
|
internal constructor() {
|
||||||
|
theData = DoubleArray(0)
|
||||||
|
theNRow = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* covariance matrix is stored in upper triangular packed storage format,
|
||||||
|
* e.g. the elements in the array are arranged like
|
||||||
|
* {a(0,0), a(0,1), a(1,1), a(0,2), a(1,2), a(2,2), ...},
|
||||||
|
* the size is nrow*(nrow+1)/2.
|
||||||
|
*/
|
||||||
|
internal constructor(data: DoubleArray, nrow: Int) {
|
||||||
|
require(data.size == nrow * (nrow + 1) / 2) { "Inconsistent arguments" }
|
||||||
|
theData = data
|
||||||
|
theNRow = nrow
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Constructor for MnUserCovariance.
|
||||||
|
*
|
||||||
|
* @param nrow a int.
|
||||||
|
*/
|
||||||
|
constructor(nrow: Int) {
|
||||||
|
theData = DoubleArray(nrow * (nrow + 1) / 2)
|
||||||
|
theNRow = nrow
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* copy.
|
||||||
|
*
|
||||||
|
* @return a [hep.dataforge.MINUIT.MnUserCovariance] object.
|
||||||
|
*/
|
||||||
|
fun copy(): MnUserCovariance {
|
||||||
|
return MnUserCovariance(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun data(): DoubleArray {
|
||||||
|
return theData
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* get.
|
||||||
|
*
|
||||||
|
* @param row a int.
|
||||||
|
* @param col a int.
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
operator fun get(row: Int, col: Int): Double {
|
||||||
|
require(!(row >= theNRow || col >= theNRow))
|
||||||
|
return if (row > col) {
|
||||||
|
theData[col + row * (row + 1) / 2]
|
||||||
|
} else {
|
||||||
|
theData[row + col * (col + 1) / 2]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* ncol.
|
||||||
|
*
|
||||||
|
* @return a int.
|
||||||
|
*/
|
||||||
|
fun ncol(): Int {
|
||||||
|
return theNRow
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* nrow.
|
||||||
|
*
|
||||||
|
* @return a int.
|
||||||
|
*/
|
||||||
|
fun nrow(): Int {
|
||||||
|
return theNRow
|
||||||
|
}
|
||||||
|
|
||||||
|
fun scale(f: Double) {
|
||||||
|
for (i in theData.indices) {
|
||||||
|
theData[i] *= f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* set.
|
||||||
|
*
|
||||||
|
* @param row a int.
|
||||||
|
* @param col a int.
|
||||||
|
* @param value a double.
|
||||||
|
*/
|
||||||
|
operator fun set(row: Int, col: Int, value: Double) {
|
||||||
|
require(!(row >= theNRow || col >= theNRow))
|
||||||
|
if (row > col) {
|
||||||
|
theData[col + row * (row + 1) / 2] = value
|
||||||
|
} else {
|
||||||
|
theData[row + col * (col + 1) / 2] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun size(): Int {
|
||||||
|
return theData.size
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
override fun toString(): String {
|
||||||
|
return MnPrint.toString(this)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import ru.inr.mass.maths.MultiFunction
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
internal class MnUserFcn(fcn: MultiFunction?, errDef: Double, trafo: MnUserTransformation) : MnFcn(fcn, errDef) {
|
||||||
|
private val theTransform: MnUserTransformation = trafo
|
||||||
|
override fun value(v: RealVector): Double {
|
||||||
|
return super.value(theTransform.transform(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,756 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import ru.inr.mass.minuit.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The class MnUserParameterState contains the MnUserParameters and the
|
||||||
|
* MnUserCovariance. It can be created on input by the user, or by MINUIT itself
|
||||||
|
* as user representable format of the result of the minimization.
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
* @author Darksnake
|
||||||
|
*/
|
||||||
|
class MnUserParameterState {
|
||||||
|
private var theCovariance: MnUserCovariance
|
||||||
|
private var theCovarianceValid = false
|
||||||
|
private var theEDM = 0.0
|
||||||
|
private var theFVal = 0.0
|
||||||
|
private var theGCCValid = false
|
||||||
|
private var theGlobalCC: MnGlobalCorrelationCoeff? = null
|
||||||
|
private var theIntCovariance: MnUserCovariance
|
||||||
|
private var theIntParameters: MutableList<Double>
|
||||||
|
private var theNFcn = 0
|
||||||
|
private var theParameters: MnUserParameters
|
||||||
|
private var theValid: Boolean
|
||||||
|
|
||||||
|
internal constructor() {
|
||||||
|
theValid = false
|
||||||
|
theCovarianceValid = false
|
||||||
|
theParameters = MnUserParameters()
|
||||||
|
theCovariance = MnUserCovariance()
|
||||||
|
theIntParameters = java.util.ArrayList<Double>()
|
||||||
|
theIntCovariance = MnUserCovariance()
|
||||||
|
}
|
||||||
|
|
||||||
|
private constructor(other: MnUserParameterState) {
|
||||||
|
theValid = other.theValid
|
||||||
|
theCovarianceValid = other.theCovarianceValid
|
||||||
|
theGCCValid = other.theGCCValid
|
||||||
|
theFVal = other.theFVal
|
||||||
|
theEDM = other.theEDM
|
||||||
|
theNFcn = other.theNFcn
|
||||||
|
theParameters = other.theParameters.copy()
|
||||||
|
theCovariance = other.theCovariance
|
||||||
|
theGlobalCC = other.theGlobalCC
|
||||||
|
theIntParameters = java.util.ArrayList<Double>(other.theIntParameters)
|
||||||
|
theIntCovariance = other.theIntCovariance.copy()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from user parameters (before minimization)
|
||||||
|
* @param par
|
||||||
|
* @param err
|
||||||
|
*/
|
||||||
|
internal constructor(par: DoubleArray, err: DoubleArray) {
|
||||||
|
theValid = true
|
||||||
|
theParameters = MnUserParameters(par, err)
|
||||||
|
theCovariance = MnUserCovariance()
|
||||||
|
theGlobalCC = MnGlobalCorrelationCoeff()
|
||||||
|
theIntParameters = java.util.ArrayList<Double>(par.size)
|
||||||
|
for (i in par.indices) {
|
||||||
|
theIntParameters.add(par[i])
|
||||||
|
}
|
||||||
|
theIntCovariance = MnUserCovariance()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal constructor(par: MnUserParameters) {
|
||||||
|
theValid = true
|
||||||
|
theParameters = par
|
||||||
|
theCovariance = MnUserCovariance()
|
||||||
|
theGlobalCC = MnGlobalCorrelationCoeff()
|
||||||
|
theIntParameters = java.util.ArrayList(par.variableParameters())
|
||||||
|
theIntCovariance = MnUserCovariance()
|
||||||
|
val i = 0
|
||||||
|
for (ipar in par.parameters()) {
|
||||||
|
if (ipar.isConst() || ipar.isFixed()) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (ipar.hasLimits()) {
|
||||||
|
theIntParameters.add(ext2int(ipar.number(), ipar.value()))
|
||||||
|
} else {
|
||||||
|
theIntParameters.add(ipar.value())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from user parameters + covariance (before minimization)
|
||||||
|
* @param nrow
|
||||||
|
* @param cov
|
||||||
|
*/
|
||||||
|
internal constructor(par: DoubleArray, cov: DoubleArray, nrow: Int) {
|
||||||
|
theValid = true
|
||||||
|
theCovarianceValid = true
|
||||||
|
theCovariance = MnUserCovariance(cov, nrow)
|
||||||
|
theGlobalCC = MnGlobalCorrelationCoeff()
|
||||||
|
theIntParameters = java.util.ArrayList<Double>(par.size)
|
||||||
|
theIntCovariance = MnUserCovariance(cov, nrow)
|
||||||
|
val err = DoubleArray(par.size)
|
||||||
|
for (i in par.indices) {
|
||||||
|
assert(theCovariance[i, i] > 0.0)
|
||||||
|
err[i] = sqrt(theCovariance[i, i])
|
||||||
|
theIntParameters.add(par[i])
|
||||||
|
}
|
||||||
|
theParameters = MnUserParameters(par, err)
|
||||||
|
assert(theCovariance.nrow() === variableParameters())
|
||||||
|
}
|
||||||
|
|
||||||
|
internal constructor(par: DoubleArray, cov: MnUserCovariance) {
|
||||||
|
theValid = true
|
||||||
|
theCovarianceValid = true
|
||||||
|
theCovariance = cov
|
||||||
|
theGlobalCC = MnGlobalCorrelationCoeff()
|
||||||
|
theIntParameters = java.util.ArrayList<Double>(par.size)
|
||||||
|
theIntCovariance = cov.copy()
|
||||||
|
require(!(theCovariance.nrow() !== variableParameters())) { "Bad covariance size" }
|
||||||
|
val err = DoubleArray(par.size)
|
||||||
|
for (i in par.indices) {
|
||||||
|
require(theCovariance[i, i] > 0.0) { "Bad covariance" }
|
||||||
|
err[i] = sqrt(theCovariance[i, i])
|
||||||
|
theIntParameters.add(par[i])
|
||||||
|
}
|
||||||
|
theParameters = MnUserParameters(par, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal constructor(par: MnUserParameters, cov: MnUserCovariance) {
|
||||||
|
theValid = true
|
||||||
|
theCovarianceValid = true
|
||||||
|
theParameters = par
|
||||||
|
theCovariance = cov
|
||||||
|
theGlobalCC = MnGlobalCorrelationCoeff()
|
||||||
|
theIntParameters = java.util.ArrayList<Double>()
|
||||||
|
theIntCovariance = cov.copy()
|
||||||
|
theIntCovariance.scale(0.5)
|
||||||
|
val i = 0
|
||||||
|
for (ipar in par.parameters()) {
|
||||||
|
if (ipar.isConst() || ipar.isFixed()) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (ipar.hasLimits()) {
|
||||||
|
theIntParameters.add(ext2int(ipar.number(), ipar.value()))
|
||||||
|
} else {
|
||||||
|
theIntParameters.add(ipar.value())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(theCovariance.nrow() === variableParameters())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* construct from internal parameters (after minimization)
|
||||||
|
* @param trafo
|
||||||
|
* @param up
|
||||||
|
*/
|
||||||
|
internal constructor(st: MinimumState, up: Double, trafo: MnUserTransformation) {
|
||||||
|
theValid = st.isValid()
|
||||||
|
theCovarianceValid = false
|
||||||
|
theGCCValid = false
|
||||||
|
theFVal = st.fval()
|
||||||
|
theEDM = st.edm()
|
||||||
|
theNFcn = st.nfcn()
|
||||||
|
theParameters = MnUserParameters()
|
||||||
|
theCovariance = MnUserCovariance()
|
||||||
|
theGlobalCC = MnGlobalCorrelationCoeff()
|
||||||
|
theIntParameters = java.util.ArrayList<Double>()
|
||||||
|
theIntCovariance = MnUserCovariance()
|
||||||
|
for (ipar in trafo.parameters()) {
|
||||||
|
if (ipar.isConst()) {
|
||||||
|
add(ipar.name(), ipar.value())
|
||||||
|
} else if (ipar.isFixed()) {
|
||||||
|
add(ipar.name(), ipar.value(), ipar.error())
|
||||||
|
if (ipar.hasLimits()) {
|
||||||
|
if (ipar.hasLowerLimit() && ipar.hasUpperLimit()) {
|
||||||
|
setLimits(ipar.name(), ipar.lowerLimit(), ipar.upperLimit())
|
||||||
|
} else if (ipar.hasLowerLimit() && !ipar.hasUpperLimit()) {
|
||||||
|
setLowerLimit(ipar.name(), ipar.lowerLimit())
|
||||||
|
} else {
|
||||||
|
setUpperLimit(ipar.name(), ipar.upperLimit())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fix(ipar.name())
|
||||||
|
} else if (ipar.hasLimits()) {
|
||||||
|
val i: Int = trafo.intOfExt(ipar.number())
|
||||||
|
val err: Double = if (st.hasCovariance()) sqrt(2.0 * up * st.error().invHessian()[i, i]) else st.parameters().dirin().getEntry(i)
|
||||||
|
add(ipar.name(),
|
||||||
|
trafo.int2ext(i, st.vec().getEntry(i)),
|
||||||
|
trafo.int2extError(i, st.vec().getEntry(i), err))
|
||||||
|
if (ipar.hasLowerLimit() && ipar.hasUpperLimit()) {
|
||||||
|
setLimits(ipar.name(), ipar.lowerLimit(), ipar.upperLimit())
|
||||||
|
} else if (ipar.hasLowerLimit() && !ipar.hasUpperLimit()) {
|
||||||
|
setLowerLimit(ipar.name(), ipar.lowerLimit())
|
||||||
|
} else {
|
||||||
|
setUpperLimit(ipar.name(), ipar.upperLimit())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val i: Int = trafo.intOfExt(ipar.number())
|
||||||
|
val err: Double = if (st.hasCovariance()) sqrt(2.0 * up * st.error().invHessian()[i, i]) else st.parameters().dirin().getEntry(i)
|
||||||
|
add(ipar.name(), st.vec().getEntry(i), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
theCovarianceValid = st.error().isValid()
|
||||||
|
if (theCovarianceValid) {
|
||||||
|
theCovariance = trafo.int2extCovariance(st.vec(), st.error().invHessian())
|
||||||
|
theIntCovariance = MnUserCovariance(st.error().invHessian().data().clone(), st.error().invHessian().nrow())
|
||||||
|
theCovariance.scale(2.0 * up)
|
||||||
|
theGlobalCC = MnGlobalCorrelationCoeff(st.error().invHessian())
|
||||||
|
theGCCValid = true
|
||||||
|
assert(theCovariance.nrow() === variableParameters())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add free parameter name, value, error
|
||||||
|
*
|
||||||
|
* @param err a double.
|
||||||
|
* @param val a double.
|
||||||
|
* @param name a [String] object.
|
||||||
|
*/
|
||||||
|
fun add(name: String, `val`: Double, err: Double) {
|
||||||
|
theParameters.add(name, `val`, err)
|
||||||
|
theIntParameters.add(`val`)
|
||||||
|
theCovarianceValid = false
|
||||||
|
theGCCValid = false
|
||||||
|
theValid = true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add limited parameter name, value, lower bound, upper bound
|
||||||
|
*
|
||||||
|
* @param name a [String] object.
|
||||||
|
* @param val a double.
|
||||||
|
* @param low a double.
|
||||||
|
* @param err a double.
|
||||||
|
* @param up a double.
|
||||||
|
*/
|
||||||
|
fun add(name: String, `val`: Double, err: Double, low: Double, up: Double) {
|
||||||
|
theParameters.add(name, `val`, err, low, up)
|
||||||
|
theCovarianceValid = false
|
||||||
|
theIntParameters.add(ext2int(index(name), `val`))
|
||||||
|
theGCCValid = false
|
||||||
|
theValid = true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add const parameter name, value
|
||||||
|
*
|
||||||
|
* @param name a [String] object.
|
||||||
|
* @param val a double.
|
||||||
|
*/
|
||||||
|
fun add(name: String, `val`: Double) {
|
||||||
|
theParameters.add(name, `val`)
|
||||||
|
theValid = true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* copy.
|
||||||
|
*
|
||||||
|
* @return a [hep.dataforge.MINUIT.MnUserParameterState] object.
|
||||||
|
*/
|
||||||
|
fun copy(): MnUserParameterState {
|
||||||
|
return MnUserParameterState(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Covariance matrix in the external representation
|
||||||
|
*
|
||||||
|
* @return a [hep.dataforge.MINUIT.MnUserCovariance] object.
|
||||||
|
*/
|
||||||
|
fun covariance(): MnUserCovariance {
|
||||||
|
return theCovariance
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the expected vertival distance to the minimum (EDM)
|
||||||
|
*
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
fun edm(): Double {
|
||||||
|
return theEDM
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* error.
|
||||||
|
*
|
||||||
|
* @param index a int.
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
fun error(index: Int): Double {
|
||||||
|
return theParameters.error(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* error.
|
||||||
|
*
|
||||||
|
* @param name a [String] object.
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
fun error(name: String?): Double {
|
||||||
|
return error(index(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* errors.
|
||||||
|
*
|
||||||
|
* @return an array of double.
|
||||||
|
*/
|
||||||
|
fun errors(): DoubleArray {
|
||||||
|
return theParameters.errors()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ext2int(i: Int, `val`: Double): Double {
|
||||||
|
return theParameters.trafo().ext2int(i, `val`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* extOfInt.
|
||||||
|
*
|
||||||
|
* @param internal a int.
|
||||||
|
* @return a int.
|
||||||
|
*/
|
||||||
|
fun extOfInt(internal: Int): Int {
|
||||||
|
return theParameters.trafo().extOfInt(internal)
|
||||||
|
}
|
||||||
|
/// interaction via external number of parameter
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* fix.
|
||||||
|
*
|
||||||
|
* @param e a int.
|
||||||
|
*/
|
||||||
|
fun fix(e: Int) {
|
||||||
|
val i = intOfExt(e)
|
||||||
|
if (theCovarianceValid) {
|
||||||
|
theCovariance = MnCovarianceSqueeze.squeeze(theCovariance, i)
|
||||||
|
theIntCovariance = MnCovarianceSqueeze.squeeze(theIntCovariance, i)
|
||||||
|
}
|
||||||
|
theIntParameters.removeAt(i)
|
||||||
|
theParameters.fix(e)
|
||||||
|
theGCCValid = false
|
||||||
|
}
|
||||||
|
/// interaction via name of parameter
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* fix.
|
||||||
|
*
|
||||||
|
* @param name a [String] object.
|
||||||
|
*/
|
||||||
|
fun fix(name: String?) {
|
||||||
|
fix(index(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the function value at the minimum
|
||||||
|
*
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
fun fval(): Double {
|
||||||
|
return theFVal
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* transformation internal <-> external
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
fun getTransformation(): MnUserTransformation {
|
||||||
|
return theParameters.trafo()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun globalCC(): MnGlobalCorrelationCoeff? {
|
||||||
|
return theGlobalCC
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns
|
||||||
|
* <CODE>true</CODE> if the the state has a valid covariance,
|
||||||
|
* <CODE>false</CODE> otherwise.
|
||||||
|
*
|
||||||
|
* @return a boolean.
|
||||||
|
*/
|
||||||
|
fun hasCovariance(): Boolean {
|
||||||
|
return theCovarianceValid
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* hasGlobalCC.
|
||||||
|
*
|
||||||
|
* @return a boolean.
|
||||||
|
*/
|
||||||
|
fun hasGlobalCC(): Boolean {
|
||||||
|
return theGCCValid
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* convert name into external number of parameter
|
||||||
|
*
|
||||||
|
* @param name a [String] object.
|
||||||
|
* @return a int.
|
||||||
|
*/
|
||||||
|
fun index(name: String?): Int {
|
||||||
|
return theParameters.index(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// transformation internal <-> external
|
||||||
|
fun int2ext(i: Int, `val`: Double): Double {
|
||||||
|
return theParameters.trafo().int2ext(i, `val`)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun intCovariance(): MnUserCovariance {
|
||||||
|
return theIntCovariance
|
||||||
|
}
|
||||||
|
|
||||||
|
fun intOfExt(ext: Int): Int {
|
||||||
|
return theParameters.trafo().intOfExt(ext)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minuit internal representation
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
fun intParameters(): List<Double> {
|
||||||
|
return theIntParameters
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns
|
||||||
|
* <CODE>true</CODE> if the the state is valid,
|
||||||
|
* <CODE>false</CODE> if not
|
||||||
|
*
|
||||||
|
* @return a boolean.
|
||||||
|
*/
|
||||||
|
fun isValid(): Boolean {
|
||||||
|
return theValid
|
||||||
|
}
|
||||||
|
|
||||||
|
// facade: forward interface of MnUserParameters and MnUserTransformation
|
||||||
|
fun minuitParameters(): List<MinuitParameter> {
|
||||||
|
return theParameters.parameters()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* convert external number into name of parameter
|
||||||
|
*
|
||||||
|
* @param index a int.
|
||||||
|
* @return a [String] object.
|
||||||
|
*/
|
||||||
|
fun name(index: Int): String {
|
||||||
|
return theParameters.name(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of function calls during the minimization.
|
||||||
|
*
|
||||||
|
* @return a int.
|
||||||
|
*/
|
||||||
|
fun nfcn(): Int {
|
||||||
|
return theNFcn
|
||||||
|
}
|
||||||
|
|
||||||
|
fun parameter(i: Int): MinuitParameter {
|
||||||
|
return theParameters.parameter(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
//user external representation
|
||||||
|
fun parameters(): MnUserParameters {
|
||||||
|
return theParameters
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* access to parameters and errors in column-wise representation
|
||||||
|
*
|
||||||
|
* @return an array of double.
|
||||||
|
*/
|
||||||
|
fun params(): DoubleArray {
|
||||||
|
return theParameters.params()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* precision.
|
||||||
|
*
|
||||||
|
* @return a [hep.dataforge.MINUIT.MnMachinePrecision] object.
|
||||||
|
*/
|
||||||
|
fun precision(): MnMachinePrecision {
|
||||||
|
return theParameters.precision()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* release.
|
||||||
|
*
|
||||||
|
* @param e a int.
|
||||||
|
*/
|
||||||
|
fun release(e: Int) {
|
||||||
|
theParameters.release(e)
|
||||||
|
theCovarianceValid = false
|
||||||
|
theGCCValid = false
|
||||||
|
val i = intOfExt(e)
|
||||||
|
if (parameter(e).hasLimits()) {
|
||||||
|
theIntParameters.add(i, ext2int(e, parameter(e).value()))
|
||||||
|
} else {
|
||||||
|
theIntParameters.add(i, parameter(e).value())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* release.
|
||||||
|
*
|
||||||
|
* @param name a [String] object.
|
||||||
|
*/
|
||||||
|
fun release(name: String?) {
|
||||||
|
release(index(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* removeLimits.
|
||||||
|
*
|
||||||
|
* @param e a int.
|
||||||
|
*/
|
||||||
|
fun removeLimits(e: Int) {
|
||||||
|
theParameters.removeLimits(e)
|
||||||
|
theCovarianceValid = false
|
||||||
|
theGCCValid = false
|
||||||
|
if (!parameter(e).isFixed() && !parameter(e).isConst()) {
|
||||||
|
theIntParameters[intOfExt(e)] = value(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* removeLimits.
|
||||||
|
*
|
||||||
|
* @param name a [String] object.
|
||||||
|
*/
|
||||||
|
fun removeLimits(name: String?) {
|
||||||
|
removeLimits(index(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setError.
|
||||||
|
*
|
||||||
|
* @param e a int.
|
||||||
|
* @param err a double.
|
||||||
|
* @param err a double.
|
||||||
|
*/
|
||||||
|
fun setError(e: Int, err: Double) {
|
||||||
|
theParameters.setError(e, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setError.
|
||||||
|
*
|
||||||
|
* @param name a [String] object.
|
||||||
|
* @param err a double.
|
||||||
|
*/
|
||||||
|
fun setError(name: String?, err: Double) {
|
||||||
|
setError(index(name), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setLimits.
|
||||||
|
*
|
||||||
|
* @param e a int.
|
||||||
|
* @param low a double.
|
||||||
|
* @param up a double.
|
||||||
|
*/
|
||||||
|
fun setLimits(e: Int, low: Double, up: Double) {
|
||||||
|
theParameters.setLimits(e, low, up)
|
||||||
|
theCovarianceValid = false
|
||||||
|
theGCCValid = false
|
||||||
|
if (!parameter(e).isFixed() && !parameter(e).isConst()) {
|
||||||
|
val i = intOfExt(e)
|
||||||
|
if (low < theIntParameters[i] && theIntParameters[i] < up) {
|
||||||
|
theIntParameters[i] = ext2int(e, theIntParameters[i])
|
||||||
|
} else {
|
||||||
|
theIntParameters[i] = ext2int(e, 0.5 * (low + up))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setLimits.
|
||||||
|
*
|
||||||
|
* @param name a [String] object.
|
||||||
|
* @param low a double.
|
||||||
|
* @param up a double.
|
||||||
|
*/
|
||||||
|
fun setLimits(name: String?, low: Double, up: Double) {
|
||||||
|
setLimits(index(name), low, up)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setLowerLimit.
|
||||||
|
*
|
||||||
|
* @param e a int.
|
||||||
|
* @param low a double.
|
||||||
|
*/
|
||||||
|
fun setLowerLimit(e: Int, low: Double) {
|
||||||
|
theParameters.setLowerLimit(e, low)
|
||||||
|
theCovarianceValid = false
|
||||||
|
theGCCValid = false
|
||||||
|
if (!parameter(e).isFixed() && !parameter(e).isConst()) {
|
||||||
|
val i = intOfExt(e)
|
||||||
|
if (low < theIntParameters[i]) {
|
||||||
|
theIntParameters[i] = ext2int(e, theIntParameters[i])
|
||||||
|
} else {
|
||||||
|
theIntParameters[i] = ext2int(e, low + 0.5 * abs(low + 1.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setLowerLimit.
|
||||||
|
*
|
||||||
|
* @param name a [String] object.
|
||||||
|
* @param low a double.
|
||||||
|
*/
|
||||||
|
fun setLowerLimit(name: String?, low: Double) {
|
||||||
|
setLowerLimit(index(name), low)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setPrecision.
|
||||||
|
*
|
||||||
|
* @param eps a double.
|
||||||
|
*/
|
||||||
|
fun setPrecision(eps: Double) {
|
||||||
|
theParameters.setPrecision(eps)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setUpperLimit.
|
||||||
|
*
|
||||||
|
* @param e a int.
|
||||||
|
* @param up a double.
|
||||||
|
*/
|
||||||
|
fun setUpperLimit(e: Int, up: Double) {
|
||||||
|
theParameters.setUpperLimit(e, up)
|
||||||
|
theCovarianceValid = false
|
||||||
|
theGCCValid = false
|
||||||
|
if (!parameter(e).isFixed() && !parameter(e).isConst()) {
|
||||||
|
val i = intOfExt(e)
|
||||||
|
if (theIntParameters[i] < up) {
|
||||||
|
theIntParameters[i] = ext2int(e, theIntParameters[i])
|
||||||
|
} else {
|
||||||
|
theIntParameters[i] = ext2int(e, up - 0.5 * abs(up + 1.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setUpperLimit.
|
||||||
|
*
|
||||||
|
* @param name a [String] object.
|
||||||
|
* @param up a double.
|
||||||
|
*/
|
||||||
|
fun setUpperLimit(name: String?, up: Double) {
|
||||||
|
setUpperLimit(index(name), up)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setValue.
|
||||||
|
*
|
||||||
|
* @param e a int.
|
||||||
|
* @param val a double.
|
||||||
|
*/
|
||||||
|
fun setValue(e: Int, `val`: Double) {
|
||||||
|
theParameters.setValue(e, `val`)
|
||||||
|
if (!parameter(e).isFixed() && !parameter(e).isConst()) {
|
||||||
|
val i = intOfExt(e)
|
||||||
|
if (parameter(e).hasLimits()) {
|
||||||
|
theIntParameters[i] = ext2int(e, `val`)
|
||||||
|
} else {
|
||||||
|
theIntParameters[i] = `val`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setValue.
|
||||||
|
*
|
||||||
|
* @param name a [String] object.
|
||||||
|
* @param val a double.
|
||||||
|
*/
|
||||||
|
fun setValue(name: String?, `val`: Double) {
|
||||||
|
setValue(index(name), `val`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
override fun toString(): String {
|
||||||
|
return MnPrint.toString(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* value.
|
||||||
|
*
|
||||||
|
* @param index a int.
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
fun value(index: Int): Double {
|
||||||
|
return theParameters.value(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* value.
|
||||||
|
*
|
||||||
|
* @param name a [String] object.
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
fun value(name: String?): Double {
|
||||||
|
return value(index(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* variableParameters.
|
||||||
|
*
|
||||||
|
* @return a int.
|
||||||
|
*/
|
||||||
|
fun variableParameters(): Int {
|
||||||
|
return theParameters.variableParameters()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,402 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API class for the user interaction with the parameters. Serves as input to
|
||||||
|
* the minimizer as well as output from it; users can interact: fix/release
|
||||||
|
* parameters, set values and errors, etc.; parameters can be accessed via their
|
||||||
|
* parameter number or via their user-specified name.
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
* @author Darksnake
|
||||||
|
*/
|
||||||
|
class MnUserParameters {
|
||||||
|
private var theTransformation: MnUserTransformation
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of MnUserParameters
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
theTransformation = MnUserTransformation()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Constructor for MnUserParameters.
|
||||||
|
*
|
||||||
|
* @param par an array of double.
|
||||||
|
* @param err an array of double.
|
||||||
|
*/
|
||||||
|
constructor(par: DoubleArray, err: DoubleArray) {
|
||||||
|
theTransformation = MnUserTransformation(par, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
private constructor(other: MnUserParameters) {
|
||||||
|
theTransformation = other.theTransformation.copy()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add free parameter name, value, error
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* When adding parameters, MINUIT assigns indices to each parameter which
|
||||||
|
* will be the same as in the double[] in the
|
||||||
|
* MultiFunction.valueOf(). That means the first parameter the user
|
||||||
|
* adds gets index 0, the second index 1, and so on. When calculating the
|
||||||
|
* function value inside FCN, MINUIT will call
|
||||||
|
* MultiFunction.valueOf() with the elements at their respective
|
||||||
|
* positions.
|
||||||
|
*
|
||||||
|
* @param err a double.
|
||||||
|
* @param val a double.
|
||||||
|
* @param name a [String] object.
|
||||||
|
*/
|
||||||
|
fun add(name: String, `val`: Double, err: Double) {
|
||||||
|
theTransformation.add(name, `val`, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add limited parameter name, value, lower bound, upper bound
|
||||||
|
*
|
||||||
|
* @param up a double.
|
||||||
|
* @param low a double.
|
||||||
|
* @param name a [String] object.
|
||||||
|
* @param val a double.
|
||||||
|
* @param err a double.
|
||||||
|
*/
|
||||||
|
fun add(name: String, `val`: Double, err: Double, low: Double, up: Double) {
|
||||||
|
theTransformation.add(name, `val`, err, low, up)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add const parameter name, value
|
||||||
|
*
|
||||||
|
* @param name a [String] object.
|
||||||
|
* @param val a double.
|
||||||
|
*/
|
||||||
|
fun add(name: String, `val`: Double) {
|
||||||
|
theTransformation.add(name, `val`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* copy.
|
||||||
|
*
|
||||||
|
* @return a [hep.dataforge.MINUIT.MnUserParameters] object.
|
||||||
|
*/
|
||||||
|
fun copy(): MnUserParameters {
|
||||||
|
return MnUserParameters(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* error.
|
||||||
|
*
|
||||||
|
* @param index a int.
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
fun error(index: Int): Double {
|
||||||
|
return theTransformation.error(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* error.
|
||||||
|
*
|
||||||
|
* @param name a [String] object.
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
fun error(name: String?): Double {
|
||||||
|
return theTransformation.error(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun errors(): DoubleArray {
|
||||||
|
return theTransformation.errors()
|
||||||
|
}
|
||||||
|
/// interaction via external number of parameter
|
||||||
|
/**
|
||||||
|
* Fixes the specified parameter (so that the minimizer will no longer vary
|
||||||
|
* it)
|
||||||
|
*
|
||||||
|
* @param index a int.
|
||||||
|
*/
|
||||||
|
fun fix(index: Int) {
|
||||||
|
theTransformation.fix(index)
|
||||||
|
}
|
||||||
|
/// interaction via name of parameter
|
||||||
|
/**
|
||||||
|
* Fixes the specified parameter (so that the minimizer will no longer vary
|
||||||
|
* it)
|
||||||
|
*
|
||||||
|
* @param name a [String] object.
|
||||||
|
*/
|
||||||
|
fun fix(name: String?) {
|
||||||
|
theTransformation.fix(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* convert name into external number of parameter
|
||||||
|
* @param name
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
fun index(name: String?): Int {
|
||||||
|
return theTransformation.index(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* convert external number into name of parameter
|
||||||
|
* @param index
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
fun name(index: Int): String {
|
||||||
|
return theTransformation.name(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* access to single parameter
|
||||||
|
* @param index
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
fun parameter(index: Int): MinuitParameter {
|
||||||
|
return theTransformation.parameter(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* access to parameters (row-wise)
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
fun parameters(): List<MinuitParameter> {
|
||||||
|
return theTransformation.parameters()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* access to parameters and errors in column-wise representation
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
fun params(): DoubleArray {
|
||||||
|
return theTransformation.params()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* precision.
|
||||||
|
*
|
||||||
|
* @return a [hep.dataforge.MINUIT.MnMachinePrecision] object.
|
||||||
|
*/
|
||||||
|
fun precision(): MnMachinePrecision {
|
||||||
|
return theTransformation.precision()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Releases the specified parameter (so that the minimizer can vary it)
|
||||||
|
*
|
||||||
|
* @param index a int.
|
||||||
|
*/
|
||||||
|
fun release(index: Int) {
|
||||||
|
theTransformation.release(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Releases the specified parameter (so that the minimizer can vary it)
|
||||||
|
*
|
||||||
|
* @param name a [String] object.
|
||||||
|
*/
|
||||||
|
fun release(name: String?) {
|
||||||
|
theTransformation.release(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* removeLimits.
|
||||||
|
*
|
||||||
|
* @param index a int.
|
||||||
|
*/
|
||||||
|
fun removeLimits(index: Int) {
|
||||||
|
theTransformation.removeLimits(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* removeLimits.
|
||||||
|
*
|
||||||
|
* @param name a [String] object.
|
||||||
|
*/
|
||||||
|
fun removeLimits(name: String?) {
|
||||||
|
theTransformation.removeLimits(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setError.
|
||||||
|
*
|
||||||
|
* @param index a int.
|
||||||
|
* @param err a double.
|
||||||
|
*/
|
||||||
|
fun setError(index: Int, err: Double) {
|
||||||
|
theTransformation.setError(index, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setError.
|
||||||
|
*
|
||||||
|
* @param name a [String] object.
|
||||||
|
* @param err a double.
|
||||||
|
*/
|
||||||
|
fun setError(name: String?, err: Double) {
|
||||||
|
theTransformation.setError(name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the lower and upper bound on the specified variable.
|
||||||
|
*
|
||||||
|
* @param up a double.
|
||||||
|
* @param low a double.
|
||||||
|
* @param index a int.
|
||||||
|
*/
|
||||||
|
fun setLimits(index: Int, low: Double, up: Double) {
|
||||||
|
theTransformation.setLimits(index, low, up)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the lower and upper bound on the specified variable.
|
||||||
|
*
|
||||||
|
* @param up a double.
|
||||||
|
* @param low a double.
|
||||||
|
* @param name a [String] object.
|
||||||
|
*/
|
||||||
|
fun setLimits(name: String?, low: Double, up: Double) {
|
||||||
|
theTransformation.setLimits(name, low, up)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setLowerLimit.
|
||||||
|
*
|
||||||
|
* @param index a int.
|
||||||
|
* @param low a double.
|
||||||
|
*/
|
||||||
|
fun setLowerLimit(index: Int, low: Double) {
|
||||||
|
theTransformation.setLowerLimit(index, low)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setLowerLimit.
|
||||||
|
*
|
||||||
|
* @param name a [String] object.
|
||||||
|
* @param low a double.
|
||||||
|
*/
|
||||||
|
fun setLowerLimit(name: String?, low: Double) {
|
||||||
|
theTransformation.setLowerLimit(name, low)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setPrecision.
|
||||||
|
*
|
||||||
|
* @param eps a double.
|
||||||
|
*/
|
||||||
|
fun setPrecision(eps: Double) {
|
||||||
|
theTransformation.setPrecision(eps)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setUpperLimit.
|
||||||
|
*
|
||||||
|
* @param index a int.
|
||||||
|
* @param up a double.
|
||||||
|
*/
|
||||||
|
fun setUpperLimit(index: Int, up: Double) {
|
||||||
|
theTransformation.setUpperLimit(index, up)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* setUpperLimit.
|
||||||
|
*
|
||||||
|
* @param name a [String] object.
|
||||||
|
* @param up a double.
|
||||||
|
*/
|
||||||
|
fun setUpperLimit(name: String?, up: Double) {
|
||||||
|
theTransformation.setUpperLimit(name, up)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the value of parameter. The parameter in question may be variable,
|
||||||
|
* fixed, or constant, but must be defined.
|
||||||
|
*
|
||||||
|
* @param index a int.
|
||||||
|
* @param val a double.
|
||||||
|
*/
|
||||||
|
fun setValue(index: Int, `val`: Double) {
|
||||||
|
theTransformation.setValue(index, `val`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the value of parameter. The parameter in question may be variable,
|
||||||
|
* fixed, or constant, but must be defined.
|
||||||
|
*
|
||||||
|
* @param name a [String] object.
|
||||||
|
* @param val a double.
|
||||||
|
*/
|
||||||
|
fun setValue(name: String?, `val`: Double) {
|
||||||
|
theTransformation.setValue(name, `val`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
override fun toString(): String {
|
||||||
|
return MnPrint.toString(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun trafo(): MnUserTransformation {
|
||||||
|
return theTransformation
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* value.
|
||||||
|
*
|
||||||
|
* @param index a int.
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
fun value(index: Int): Double {
|
||||||
|
return theTransformation.value(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* value.
|
||||||
|
*
|
||||||
|
* @param name a [String] object.
|
||||||
|
* @return a double.
|
||||||
|
*/
|
||||||
|
fun value(name: String?): Double {
|
||||||
|
return theTransformation.value(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* variableParameters.
|
||||||
|
*
|
||||||
|
* @return a int.
|
||||||
|
*/
|
||||||
|
fun variableParameters(): Int {
|
||||||
|
return theTransformation.variableParameters()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,390 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import org.apache.commons.math3.linear.ArrayRealVector
|
||||||
|
|
||||||
|
/**
|
||||||
|
* knows how to andThen between user specified parameters (external) and
|
||||||
|
* internal parameters used for minimization
|
||||||
|
*
|
||||||
|
* Жуткий октопус, который занимается преобразованием внешних параметров во внутренние
|
||||||
|
* TODO по возможности отказаться от использования этого монстра
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
class MnUserTransformation {
|
||||||
|
private val nameMap: MutableMap<String?, Int> = HashMap()
|
||||||
|
private var theCache: MutableList<Double>
|
||||||
|
private var theExtOfInt: MutableList<Int>
|
||||||
|
private var theParameters: MutableList<MinuitParameter>
|
||||||
|
private var thePrecision: MnMachinePrecision
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
thePrecision = MnMachinePrecision()
|
||||||
|
theParameters = java.util.ArrayList<MinuitParameter>()
|
||||||
|
theExtOfInt = java.util.ArrayList<Int>()
|
||||||
|
theCache = java.util.ArrayList<Double>(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
private constructor(other: MnUserTransformation) {
|
||||||
|
thePrecision = other.thePrecision
|
||||||
|
theParameters = java.util.ArrayList<MinuitParameter>(other.theParameters.size)
|
||||||
|
for (par in other.theParameters) {
|
||||||
|
theParameters.add(par.copy())
|
||||||
|
}
|
||||||
|
theExtOfInt = java.util.ArrayList<Int>(other.theExtOfInt)
|
||||||
|
theCache = java.util.ArrayList<Double>(other.theCache)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(par: DoubleArray, err: DoubleArray) {
|
||||||
|
thePrecision = MnMachinePrecision()
|
||||||
|
theParameters = java.util.ArrayList<MinuitParameter>(par.size)
|
||||||
|
theExtOfInt = java.util.ArrayList<Int>(par.size)
|
||||||
|
theCache = java.util.ArrayList<Double>(par.size)
|
||||||
|
for (i in par.indices) {
|
||||||
|
add("p$i", par[i], err[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add free parameter
|
||||||
|
* @param err
|
||||||
|
* @param val
|
||||||
|
*/
|
||||||
|
fun add(name: String, `val`: Double, err: Double) {
|
||||||
|
require(!nameMap.containsKey(name)) { "duplicate name: $name" }
|
||||||
|
nameMap[name] = theParameters.size
|
||||||
|
theExtOfInt.add(theParameters.size)
|
||||||
|
theCache.add(`val`)
|
||||||
|
theParameters.add(MinuitParameter(theParameters.size, name, `val`, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add limited parameter
|
||||||
|
* @param up
|
||||||
|
* @param low
|
||||||
|
*/
|
||||||
|
fun add(name: String, `val`: Double, err: Double, low: Double, up: Double) {
|
||||||
|
require(!nameMap.containsKey(name)) { "duplicate name: $name" }
|
||||||
|
nameMap[name] = theParameters.size
|
||||||
|
theExtOfInt.add(theParameters.size)
|
||||||
|
theCache.add(`val`)
|
||||||
|
theParameters.add(MinuitParameter(theParameters.size, name, `val`, err, low, up))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add parameter
|
||||||
|
* @param name
|
||||||
|
* @param val
|
||||||
|
*/
|
||||||
|
fun add(name: String, `val`: Double) {
|
||||||
|
require(!nameMap.containsKey(name)) { "duplicate name: $name" }
|
||||||
|
nameMap[name] = theParameters.size
|
||||||
|
theCache.add(`val`)
|
||||||
|
theParameters.add(MinuitParameter(theParameters.size, name, `val`))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* copy.
|
||||||
|
*
|
||||||
|
* @return a [hep.dataforge.MINUIT.MnUserTransformation] object.
|
||||||
|
*/
|
||||||
|
fun copy(): MnUserTransformation {
|
||||||
|
return MnUserTransformation(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun dInt2Ext(i: Int, `val`: Double): Double {
|
||||||
|
var dd = 1.0
|
||||||
|
val parm: MinuitParameter = theParameters[theExtOfInt[i]]
|
||||||
|
if (parm.hasLimits()) {
|
||||||
|
dd = if (parm.hasUpperLimit() && parm.hasLowerLimit()) {
|
||||||
|
theDoubleLimTrafo.dInt2Ext(`val`,
|
||||||
|
parm.upperLimit(),
|
||||||
|
parm.lowerLimit())
|
||||||
|
} else if (parm.hasUpperLimit() && !parm.hasLowerLimit()) {
|
||||||
|
theUpperLimTrafo.dInt2Ext(`val`, parm.upperLimit())
|
||||||
|
} else {
|
||||||
|
theLowerLimTrafo.dInt2Ext(`val`, parm.lowerLimit())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dd
|
||||||
|
}
|
||||||
|
|
||||||
|
fun error(index: Int): Double {
|
||||||
|
return theParameters[index].error()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun error(name: String?): Double {
|
||||||
|
return error(index(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun errors(): DoubleArray {
|
||||||
|
val result = DoubleArray(theParameters.size)
|
||||||
|
var i = 0
|
||||||
|
for (parameter in theParameters) {
|
||||||
|
result[i++] = parameter.error()
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ext2int(i: Int, `val`: Double): Double {
|
||||||
|
val parm: MinuitParameter = theParameters[i]
|
||||||
|
return if (parm.hasLimits()) {
|
||||||
|
if (parm.hasUpperLimit() && parm.hasLowerLimit()) {
|
||||||
|
theDoubleLimTrafo.ext2int(`val`,
|
||||||
|
parm.upperLimit(),
|
||||||
|
parm.lowerLimit(),
|
||||||
|
precision())
|
||||||
|
} else if (parm.hasUpperLimit() && !parm.hasLowerLimit()) {
|
||||||
|
theUpperLimTrafo.ext2int(`val`,
|
||||||
|
parm.upperLimit(),
|
||||||
|
precision())
|
||||||
|
} else {
|
||||||
|
theLowerLimTrafo.ext2int(`val`,
|
||||||
|
parm.lowerLimit(),
|
||||||
|
precision())
|
||||||
|
}
|
||||||
|
} else `val`
|
||||||
|
}
|
||||||
|
|
||||||
|
fun extOfInt(internal: Int): Int {
|
||||||
|
return theExtOfInt[internal]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* interaction via external number of parameter
|
||||||
|
* @param index
|
||||||
|
*/
|
||||||
|
fun fix(index: Int) {
|
||||||
|
val iind = intOfExt(index)
|
||||||
|
theExtOfInt.removeAt(iind)
|
||||||
|
theParameters[index].fix()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* interaction via name of parameter
|
||||||
|
* @param name
|
||||||
|
*/
|
||||||
|
fun fix(name: String?) {
|
||||||
|
fix(index(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* convert name into external number of parameter
|
||||||
|
* @param name
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
fun index(name: String?): Int {
|
||||||
|
return nameMap[name]!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun int2ext(i: Int, `val`: Double): Double {
|
||||||
|
val parm: MinuitParameter = theParameters[theExtOfInt[i]]
|
||||||
|
return if (parm.hasLimits()) {
|
||||||
|
if (parm.hasUpperLimit() && parm.hasLowerLimit()) {
|
||||||
|
theDoubleLimTrafo.int2ext(`val`,
|
||||||
|
parm.upperLimit(),
|
||||||
|
parm.lowerLimit())
|
||||||
|
} else if (parm.hasUpperLimit() && !parm.hasLowerLimit()) {
|
||||||
|
theUpperLimTrafo.int2ext(`val`, parm.upperLimit())
|
||||||
|
} else {
|
||||||
|
theLowerLimTrafo.int2ext(`val`, parm.lowerLimit())
|
||||||
|
}
|
||||||
|
} else `val`
|
||||||
|
}
|
||||||
|
|
||||||
|
fun int2extCovariance(vec: RealVector, cov: MnAlgebraicSymMatrix): MnUserCovariance {
|
||||||
|
val result = MnUserCovariance(cov.nrow())
|
||||||
|
for (i in 0 until vec.getDimension()) {
|
||||||
|
var dxdi = 1.0
|
||||||
|
if (theParameters[theExtOfInt[i]].hasLimits()) {
|
||||||
|
dxdi = dInt2Ext(i, vec.getEntry(i))
|
||||||
|
}
|
||||||
|
for (j in i until vec.getDimension()) {
|
||||||
|
var dxdj = 1.0
|
||||||
|
if (theParameters[theExtOfInt[j]].hasLimits()) {
|
||||||
|
dxdj = dInt2Ext(j, vec.getEntry(j))
|
||||||
|
}
|
||||||
|
result[i, j] = dxdi * cov[i, j] * dxdj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun int2extError(i: Int, `val`: Double, err: Double): Double {
|
||||||
|
var dx = err
|
||||||
|
val parm: MinuitParameter = theParameters[theExtOfInt[i]]
|
||||||
|
if (parm.hasLimits()) {
|
||||||
|
val ui = int2ext(i, `val`)
|
||||||
|
var du1 = int2ext(i, `val` + dx) - ui
|
||||||
|
val du2 = int2ext(i, `val` - dx) - ui
|
||||||
|
if (parm.hasUpperLimit() && parm.hasLowerLimit()) {
|
||||||
|
if (dx > 1.0) {
|
||||||
|
du1 = parm.upperLimit() - parm.lowerLimit()
|
||||||
|
}
|
||||||
|
dx = 0.5 * (abs(du1) + abs(du2))
|
||||||
|
} else {
|
||||||
|
dx = 0.5 * (abs(du1) + abs(du2))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dx
|
||||||
|
}
|
||||||
|
|
||||||
|
fun intOfExt(ext: Int): Int {
|
||||||
|
for (iind in theExtOfInt.indices) {
|
||||||
|
if (ext == theExtOfInt[iind]) {
|
||||||
|
return iind
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw IllegalArgumentException("ext=$ext")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* convert external number into name of parameter
|
||||||
|
* @param index
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
fun name(index: Int): String {
|
||||||
|
return theParameters[index].name()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* access to single parameter
|
||||||
|
* @param index
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
fun parameter(index: Int): MinuitParameter {
|
||||||
|
return theParameters[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun parameters(): List<MinuitParameter> {
|
||||||
|
return theParameters
|
||||||
|
}
|
||||||
|
|
||||||
|
//access to parameters and errors in column-wise representation
|
||||||
|
fun params(): DoubleArray {
|
||||||
|
val result = DoubleArray(theParameters.size)
|
||||||
|
var i = 0
|
||||||
|
for (parameter in theParameters) {
|
||||||
|
result[i++] = parameter.value()
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun precision(): MnMachinePrecision {
|
||||||
|
return thePrecision
|
||||||
|
}
|
||||||
|
|
||||||
|
fun release(index: Int) {
|
||||||
|
require(!theExtOfInt.contains(index)) { "index=$index" }
|
||||||
|
theExtOfInt.add(index)
|
||||||
|
Collections.sort<Int>(theExtOfInt)
|
||||||
|
theParameters[index].release()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun release(name: String?) {
|
||||||
|
release(index(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeLimits(index: Int) {
|
||||||
|
theParameters[index].removeLimits()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeLimits(name: String?) {
|
||||||
|
removeLimits(index(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setError(index: Int, err: Double) {
|
||||||
|
theParameters[index].setError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setError(name: String?, err: Double) {
|
||||||
|
setError(index(name), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setLimits(index: Int, low: Double, up: Double) {
|
||||||
|
theParameters[index].setLimits(low, up)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setLimits(name: String?, low: Double, up: Double) {
|
||||||
|
setLimits(index(name), low, up)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setLowerLimit(index: Int, low: Double) {
|
||||||
|
theParameters[index].setLowerLimit(low)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setLowerLimit(name: String?, low: Double) {
|
||||||
|
setLowerLimit(index(name), low)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setPrecision(eps: Double) {
|
||||||
|
thePrecision.setPrecision(eps)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setUpperLimit(index: Int, up: Double) {
|
||||||
|
theParameters[index].setUpperLimit(up)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setUpperLimit(name: String?, up: Double) {
|
||||||
|
setUpperLimit(index(name), up)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setValue(index: Int, `val`: Double) {
|
||||||
|
theParameters[index].setValue(`val`)
|
||||||
|
theCache[index] = `val`
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setValue(name: String?, `val`: Double) {
|
||||||
|
setValue(index(name), `val`)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun transform(pstates: RealVector): ArrayRealVector {
|
||||||
|
// FixMe: Worry about efficiency here
|
||||||
|
val result = ArrayRealVector(theCache.size)
|
||||||
|
for (i in 0 until result.getDimension()) {
|
||||||
|
result.setEntry(i, theCache[i])
|
||||||
|
}
|
||||||
|
for (i in 0 until pstates.getDimension()) {
|
||||||
|
if (theParameters[theExtOfInt[i]].hasLimits()) {
|
||||||
|
result.setEntry(theExtOfInt[i], int2ext(i, pstates.getEntry(i)))
|
||||||
|
} else {
|
||||||
|
result.setEntry(theExtOfInt[i], pstates.getEntry(i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
//forwarded interface
|
||||||
|
fun value(index: Int): Double {
|
||||||
|
return theParameters[index].value()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun value(name: String?): Double {
|
||||||
|
return value(index(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun variableParameters(): Int {
|
||||||
|
return theExtOfInt.size
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val theDoubleLimTrafo: SinParameterTransformation = SinParameterTransformation()
|
||||||
|
private val theLowerLimTrafo: SqrtLowParameterTransformation = SqrtLowParameterTransformation()
|
||||||
|
private val theUpperLimTrafo: SqrtUpParameterTransformation = SqrtUpParameterTransformation()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,147 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import org.apache.commons.math3.linear.ArrayRealVector
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utilities for operating on vectors and matrices
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
internal object MnUtils {
|
||||||
|
fun absoluteSumOfElements(m: MnAlgebraicSymMatrix): Double {
|
||||||
|
val data: DoubleArray = m.data()
|
||||||
|
var result = 0.0
|
||||||
|
for (i in data.indices) {
|
||||||
|
result += abs(data[i])
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun add(v1: RealVector, v2: RealVector?): RealVector {
|
||||||
|
return v1.add(v2)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun add(m1: MnAlgebraicSymMatrix, m2: MnAlgebraicSymMatrix): MnAlgebraicSymMatrix {
|
||||||
|
require(!(m1.size() !== m2.size())) { "Incompatible matrices" }
|
||||||
|
val result: MnAlgebraicSymMatrix = m1.copy()
|
||||||
|
val a: DoubleArray = result.data()
|
||||||
|
val b: DoubleArray = m2.data()
|
||||||
|
for (i in a.indices) {
|
||||||
|
a[i] += b[i]
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun div(m: MnAlgebraicSymMatrix?, scale: Double): MnAlgebraicSymMatrix {
|
||||||
|
return mul(m, 1 / scale)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun div(m: RealVector?, scale: Double): RealVector {
|
||||||
|
return mul(m, 1 / scale)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun innerProduct(v1: RealVector, v2: RealVector): Double {
|
||||||
|
require(!(v1.getDimension() !== v2.getDimension())) { "Incompatible vectors" }
|
||||||
|
var total = 0.0
|
||||||
|
for (i in 0 until v1.getDimension()) {
|
||||||
|
total += v1.getEntry(i) * v2.getEntry(i)
|
||||||
|
}
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
fun mul(v1: RealVector, scale: Double): RealVector {
|
||||||
|
return v1.mapMultiply(scale)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun mul(m1: MnAlgebraicSymMatrix, scale: Double): MnAlgebraicSymMatrix {
|
||||||
|
val result: MnAlgebraicSymMatrix = m1.copy()
|
||||||
|
val a: DoubleArray = result.data()
|
||||||
|
for (i in a.indices) {
|
||||||
|
a[i] *= scale
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun mul(m1: MnAlgebraicSymMatrix, v1: RealVector): ArrayRealVector {
|
||||||
|
require(!(m1.nrow() !== v1.getDimension())) { "Incompatible arguments" }
|
||||||
|
val result = ArrayRealVector(m1.nrow())
|
||||||
|
for (i in 0 until result.getDimension()) {
|
||||||
|
var total = 0.0
|
||||||
|
for (k in 0 until result.getDimension()) {
|
||||||
|
total += m1[i, k] * v1.getEntry(k)
|
||||||
|
}
|
||||||
|
result.setEntry(i, total)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun mul(m1: MnAlgebraicSymMatrix, m2: MnAlgebraicSymMatrix): MnAlgebraicSymMatrix {
|
||||||
|
require(!(m1.size() !== m2.size())) { "Incompatible matrices" }
|
||||||
|
val n: Int = m1.nrow()
|
||||||
|
val result = MnAlgebraicSymMatrix(n)
|
||||||
|
for (i in 0 until n) {
|
||||||
|
for (j in 0..i) {
|
||||||
|
var total = 0.0
|
||||||
|
for (k in 0 until n) {
|
||||||
|
total += m1[i, k] * m2[k, j]
|
||||||
|
}
|
||||||
|
result[i, j] = total
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun outerProduct(v2: RealVector): MnAlgebraicSymMatrix {
|
||||||
|
// Fixme: check this. I am assuming this is just an outer-product of vector
|
||||||
|
// with itself.
|
||||||
|
val n: Int = v2.getDimension()
|
||||||
|
val result = MnAlgebraicSymMatrix(n)
|
||||||
|
val data: DoubleArray = v2.toArray()
|
||||||
|
for (i in 0 until n) {
|
||||||
|
for (j in 0..i) {
|
||||||
|
result[i, j] = data[i] * data[j]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun similarity(avec: RealVector, mat: MnAlgebraicSymMatrix): Double {
|
||||||
|
val n: Int = avec.getDimension()
|
||||||
|
val tmp: RealVector = mul(mat, avec)
|
||||||
|
var result = 0.0
|
||||||
|
for (i in 0 until n) {
|
||||||
|
result += tmp.getEntry(i) * avec.getEntry(i)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sub(v1: RealVector, v2: RealVector?): RealVector {
|
||||||
|
return v1.subtract(v2)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sub(m1: MnAlgebraicSymMatrix, m2: MnAlgebraicSymMatrix): MnAlgebraicSymMatrix {
|
||||||
|
require(!(m1.size() !== m2.size())) { "Incompatible matrices" }
|
||||||
|
val result: MnAlgebraicSymMatrix = m1.copy()
|
||||||
|
val a: DoubleArray = result.data()
|
||||||
|
val b: DoubleArray = m2.data()
|
||||||
|
for (i in a.indices) {
|
||||||
|
a[i] -= b[i]
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import ru.inr.mass.maths.MultiFunction
|
||||||
|
import ru.inr.mass.minuit.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
abstract class ModularFunctionMinimizer {
|
||||||
|
abstract fun builder(): MinimumBuilder
|
||||||
|
fun minimize(
|
||||||
|
fcn: MultiFunction?,
|
||||||
|
st: MnUserParameterState,
|
||||||
|
strategy: MnStrategy,
|
||||||
|
maxfcn: Int,
|
||||||
|
toler: Double,
|
||||||
|
errorDef: Double,
|
||||||
|
useAnalyticalGradient: Boolean,
|
||||||
|
checkGradient: Boolean
|
||||||
|
): FunctionMinimum {
|
||||||
|
var maxfcn = maxfcn
|
||||||
|
val mfcn = MnUserFcn(fcn, errorDef, st.getTransformation())
|
||||||
|
val gc: GradientCalculator
|
||||||
|
var providesAllDerivs = true
|
||||||
|
/*
|
||||||
|
* Проверяем в явном виде, что все аналитические производные присутствуют
|
||||||
|
* TODO сделать возможность того, что часть производных задается аналитически, а часть численно
|
||||||
|
*/for (i in 0 until fcn.getDimension()) {
|
||||||
|
if (!fcn.providesDeriv(i)) providesAllDerivs = false
|
||||||
|
}
|
||||||
|
gc = if (providesAllDerivs && useAnalyticalGradient) {
|
||||||
|
AnalyticalGradientCalculator(fcn, st.getTransformation(), checkGradient)
|
||||||
|
} else {
|
||||||
|
Numerical2PGradientCalculator(mfcn, st.getTransformation(), strategy)
|
||||||
|
}
|
||||||
|
val npar: Int = st.variableParameters()
|
||||||
|
if (maxfcn == 0) {
|
||||||
|
maxfcn = 200 + 100 * npar + 5 * npar * npar
|
||||||
|
}
|
||||||
|
val mnseeds: MinimumSeed = seedGenerator().generate(mfcn, gc, st, strategy)
|
||||||
|
return minimize(mfcn, gc, mnseeds, strategy, maxfcn, toler)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun minimize(
|
||||||
|
mfcn: MnFcn,
|
||||||
|
gc: GradientCalculator?,
|
||||||
|
seed: MinimumSeed?,
|
||||||
|
strategy: MnStrategy?,
|
||||||
|
maxfcn: Int,
|
||||||
|
toler: Double
|
||||||
|
): FunctionMinimum {
|
||||||
|
return builder().minimum(mfcn, gc, seed, strategy, maxfcn, toler * mfcn.errorDef())
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract fun seedGenerator(): MinimumSeedGenerator
|
||||||
|
}
|
@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import org.apache.commons.math3.linear.ArrayRealVector
|
||||||
|
import ru.inr.mass.minuit.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In case that one of the components of the second derivative g2 calculated by
|
||||||
|
* the numerical gradient calculator is negative, a 1dim line search in the
|
||||||
|
* direction of that component is done in order to find a better position where
|
||||||
|
* g2 is again positive.
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
internal object NegativeG2LineSearch {
|
||||||
|
fun hasNegativeG2(grad: FunctionGradient, prec: MnMachinePrecision): Boolean {
|
||||||
|
for (i in 0 until grad.getGradient().getDimension()) {
|
||||||
|
if (grad.getGradientDerivative().getEntry(i) < prec.eps2()) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun search(fcn: MnFcn, st: MinimumState, gc: GradientCalculator, prec: MnMachinePrecision): MinimumState {
|
||||||
|
val negG2 = hasNegativeG2(st.gradient(), prec)
|
||||||
|
if (!negG2) {
|
||||||
|
return st
|
||||||
|
}
|
||||||
|
val n: Int = st.parameters().vec().getDimension()
|
||||||
|
var dgrad: FunctionGradient = st.gradient()
|
||||||
|
var pa: MinimumParameters = st.parameters()
|
||||||
|
var iterate = false
|
||||||
|
var iter = 0
|
||||||
|
do {
|
||||||
|
iterate = false
|
||||||
|
for (i in 0 until n) {
|
||||||
|
if (dgrad.getGradientDerivative().getEntry(i) < prec.eps2()) {
|
||||||
|
// do line search if second derivative negative
|
||||||
|
var step: RealVector = ArrayRealVector(n)
|
||||||
|
step.setEntry(i, dgrad.getStep().getEntry(i) * dgrad.getGradient().getEntry(i))
|
||||||
|
if (abs(dgrad.getGradient().getEntry(i)) > prec.eps2()) {
|
||||||
|
step.setEntry(i,
|
||||||
|
step.getEntry(i) * (-1.0 / abs(dgrad.getGradient().getEntry(i))))
|
||||||
|
}
|
||||||
|
val gdel: Double = step.getEntry(i) * dgrad.getGradient().getEntry(i)
|
||||||
|
val pp: MnParabolaPoint = MnLineSearch.search(fcn, pa, step, gdel, prec)
|
||||||
|
step = MnUtils.mul(step, pp.x())
|
||||||
|
pa = MinimumParameters(MnUtils.add(pa.vec(), step), pp.y())
|
||||||
|
dgrad = gc.gradient(pa, dgrad)
|
||||||
|
iterate = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (iter++ < 2 * n && iterate)
|
||||||
|
val mat = MnAlgebraicSymMatrix(n)
|
||||||
|
for (i in 0 until n) {
|
||||||
|
mat[i, i] = if (abs(dgrad.getGradientDerivative()
|
||||||
|
.getEntry(i)) > prec.eps2()
|
||||||
|
) 1.0 / dgrad.getGradientDerivative().getEntry(i) else 1.0
|
||||||
|
}
|
||||||
|
val err = MinimumError(mat, 1.0)
|
||||||
|
val edm: Double = VariableMetricEDMEstimator().estimate(dgrad, err)
|
||||||
|
return MinimumState(pa, err, dgrad, edm, fcn.numOfCalls())
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import org.apache.commons.math3.linear.RealVector
|
||||||
|
import ru.inr.mass.minuit.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
internal class Numerical2PGradientCalculator(fcn: MnFcn, par: MnUserTransformation, stra: MnStrategy) :
|
||||||
|
GradientCalculator {
|
||||||
|
private val theFcn: MnFcn = fcn
|
||||||
|
private val theStrategy: MnStrategy
|
||||||
|
private val theTransformation: MnUserTransformation
|
||||||
|
fun fcn(): MnFcn {
|
||||||
|
return theFcn
|
||||||
|
}
|
||||||
|
|
||||||
|
fun gradTolerance(): Double {
|
||||||
|
return strategy().gradientTolerance()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
fun gradient(par: MinimumParameters): FunctionGradient {
|
||||||
|
val gc = InitialGradientCalculator(theFcn, theTransformation, theStrategy)
|
||||||
|
val gra: FunctionGradient = gc.gradient(par)
|
||||||
|
return gradient(par, gra)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
fun gradient(par: MinimumParameters, gradient: FunctionGradient): FunctionGradient {
|
||||||
|
require(par.isValid()) { "Parameters are invalid" }
|
||||||
|
val x: RealVector = par.vec().copy()
|
||||||
|
val fcnmin: Double = par.fval()
|
||||||
|
val dfmin: Double = 8.0 * precision().eps2() * (abs(fcnmin) + theFcn.errorDef())
|
||||||
|
val vrysml: Double = 8.0 * precision().eps() * precision().eps()
|
||||||
|
val n: Int = x.getDimension()
|
||||||
|
val grd: RealVector = gradient.getGradient().copy()
|
||||||
|
val g2: RealVector = gradient.getGradientDerivative().copy()
|
||||||
|
val gstep: RealVector = gradient.getStep().copy()
|
||||||
|
for (i in 0 until n) {
|
||||||
|
val xtf: Double = x.getEntry(i)
|
||||||
|
val epspri: Double = precision().eps2() + abs(grd.getEntry(i) * precision().eps2())
|
||||||
|
var stepb4 = 0.0
|
||||||
|
for (j in 0 until ncycle()) {
|
||||||
|
val optstp: Double = sqrt(dfmin / (abs(g2.getEntry(i)) + epspri))
|
||||||
|
var step: Double = max(optstp, abs(0.1 * gstep.getEntry(i)))
|
||||||
|
if (trafo().parameter(trafo().extOfInt(i)).hasLimits()) {
|
||||||
|
if (step > 0.5) {
|
||||||
|
step = 0.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val stpmax: Double = 10.0 * abs(gstep.getEntry(i))
|
||||||
|
if (step > stpmax) {
|
||||||
|
step = stpmax
|
||||||
|
}
|
||||||
|
val stpmin: Double =
|
||||||
|
max(vrysml, 8.0 * abs(precision().eps2() * x.getEntry(i)))
|
||||||
|
if (step < stpmin) {
|
||||||
|
step = stpmin
|
||||||
|
}
|
||||||
|
if (abs((step - stepb4) / step) < stepTolerance()) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
gstep.setEntry(i, step)
|
||||||
|
stepb4 = step
|
||||||
|
x.setEntry(i, xtf + step)
|
||||||
|
val fs1: Double = theFcn.value(x)
|
||||||
|
x.setEntry(i, xtf - step)
|
||||||
|
val fs2: Double = theFcn.value(x)
|
||||||
|
x.setEntry(i, xtf)
|
||||||
|
val grdb4: Double = grd.getEntry(i)
|
||||||
|
grd.setEntry(i, 0.5 * (fs1 - fs2) / step)
|
||||||
|
g2.setEntry(i, (fs1 + fs2 - 2.0 * fcnmin) / step / step)
|
||||||
|
if (abs(grdb4 - grd.getEntry(i)) / (abs(grd.getEntry(i)) + dfmin / step) < gradTolerance()) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FunctionGradient(grd, g2, gstep)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ncycle(): Int {
|
||||||
|
return strategy().gradientNCycles()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun precision(): MnMachinePrecision {
|
||||||
|
return theTransformation.precision()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stepTolerance(): Double {
|
||||||
|
return strategy().gradientStepTolerance()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun strategy(): MnStrategy {
|
||||||
|
return theStrategy
|
||||||
|
}
|
||||||
|
|
||||||
|
fun trafo(): MnUserTransformation {
|
||||||
|
return theTransformation
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
theTransformation = par
|
||||||
|
theStrategy = stra
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class representing a pair of double (x,y) or (lower,upper)
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
* @author Darksnake
|
||||||
|
*/
|
||||||
|
class Range
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Constructor for Range.
|
||||||
|
*
|
||||||
|
* @param k a double.
|
||||||
|
* @param v a double.
|
||||||
|
*/
|
||||||
|
(k: Double, v: Double) : Pair<Double?, Double?>(k, v)
|
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import org.apache.commons.math3.linear.ArrayRealVector
|
||||||
|
import ru.inr.mass.minuit.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a minimization using the simplex method of Nelder and Mead (ref.
|
||||||
|
* Comp. J. 7, 308 (1965)).
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
internal class ScanBuilder : MinimumBuilder {
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
fun minimum(
|
||||||
|
mfcn: MnFcn,
|
||||||
|
gc: GradientCalculator?,
|
||||||
|
seed: MinimumSeed,
|
||||||
|
stra: MnStrategy?,
|
||||||
|
maxfcn: Int,
|
||||||
|
toler: Double
|
||||||
|
): FunctionMinimum {
|
||||||
|
val x: RealVector = seed.parameters().vec().copy()
|
||||||
|
val upst = MnUserParameterState(seed.state(), mfcn.errorDef(), seed.trafo())
|
||||||
|
val scan = MnParameterScan(mfcn.fcn(), upst.parameters(), seed.fval())
|
||||||
|
var amin: Double = scan.fval()
|
||||||
|
val n: Int = seed.trafo().variableParameters()
|
||||||
|
val dirin: RealVector = ArrayRealVector(n)
|
||||||
|
for (i in 0 until n) {
|
||||||
|
val ext: Int = seed.trafo().extOfInt(i)
|
||||||
|
scan.scan(ext)
|
||||||
|
if (scan.fval() < amin) {
|
||||||
|
amin = scan.fval()
|
||||||
|
x.setEntry(i, seed.trafo().ext2int(ext, scan.parameters().value(ext)))
|
||||||
|
}
|
||||||
|
dirin.setEntry(i, sqrt(2.0 * mfcn.errorDef() * seed.error().invHessian()[i, i]))
|
||||||
|
}
|
||||||
|
val mp = MinimumParameters(x, dirin, amin)
|
||||||
|
val st = MinimumState(mp, 0.0, mfcn.numOfCalls())
|
||||||
|
val states: MutableList<MinimumState> = java.util.ArrayList<MinimumState>(1)
|
||||||
|
states.add(st)
|
||||||
|
return FunctionMinimum(seed, states, mfcn.errorDef())
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
internal class ScanMinimizer : ModularFunctionMinimizer() {
|
||||||
|
private val theBuilder: ScanBuilder
|
||||||
|
private val theSeedGenerator: SimplexSeedGenerator = SimplexSeedGenerator()
|
||||||
|
override fun builder(): MinimumBuilder {
|
||||||
|
return theBuilder
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun seedGenerator(): MinimumSeedGenerator {
|
||||||
|
return theSeedGenerator
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
theBuilder = ScanBuilder()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,179 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import space.kscience.kmath.optimization.minuit.MINUITPlugin
|
||||||
|
import ru.inr.mass.minuit.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
internal class SimplexBuilder : MinimumBuilder {
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
fun minimum(
|
||||||
|
mfcn: MnFcn,
|
||||||
|
gc: GradientCalculator?,
|
||||||
|
seed: MinimumSeed,
|
||||||
|
strategy: MnStrategy?,
|
||||||
|
maxfcn: Int,
|
||||||
|
minedm: Double
|
||||||
|
): FunctionMinimum {
|
||||||
|
val prec: MnMachinePrecision = seed.precision()
|
||||||
|
val x: RealVector = seed.parameters().vec().copy()
|
||||||
|
val step: RealVector = MnUtils.mul(seed.gradient().getStep(), 10.0)
|
||||||
|
val n: Int = x.getDimension()
|
||||||
|
val wg = 1.0 / n
|
||||||
|
val alpha = 1.0
|
||||||
|
val beta = 0.5
|
||||||
|
val gamma = 2.0
|
||||||
|
val rhomin = 4.0
|
||||||
|
val rhomax = 8.0
|
||||||
|
val rho1 = 1.0 + alpha
|
||||||
|
val rho2 = 1.0 + alpha * gamma
|
||||||
|
val simpl: MutableList<Pair<Double?, RealVector?>> = java.util.ArrayList<Pair<Double, RealVector>>(n + 1)
|
||||||
|
simpl.add(Pair(seed.fval(), x.copy()))
|
||||||
|
var jl = 0
|
||||||
|
var jh = 0
|
||||||
|
var amin: Double = seed.fval()
|
||||||
|
var aming: Double = seed.fval()
|
||||||
|
for (i in 0 until n) {
|
||||||
|
val dmin: Double = 8.0 * prec.eps2() * (abs(x.getEntry(i)) + prec.eps2())
|
||||||
|
if (step.getEntry(i) < dmin) {
|
||||||
|
step.setEntry(i, dmin)
|
||||||
|
}
|
||||||
|
x.setEntry(i, x.getEntry(i) + step.getEntry(i))
|
||||||
|
val tmp: Double = mfcn.value(x)
|
||||||
|
if (tmp < amin) {
|
||||||
|
amin = tmp
|
||||||
|
jl = i + 1
|
||||||
|
}
|
||||||
|
if (tmp > aming) {
|
||||||
|
aming = tmp
|
||||||
|
jh = i + 1
|
||||||
|
}
|
||||||
|
simpl.add(Pair(tmp, x.copy()))
|
||||||
|
x.setEntry(i, x.getEntry(i) - step.getEntry(i))
|
||||||
|
}
|
||||||
|
val simplex = SimplexParameters(simpl, jh, jl)
|
||||||
|
do {
|
||||||
|
amin = simplex[jl].getFirst()
|
||||||
|
jl = simplex.jl()
|
||||||
|
jh = simplex.jh()
|
||||||
|
var pbar: RealVector = ArrayRealVector(n)
|
||||||
|
for (i in 0 until n + 1) {
|
||||||
|
if (i == jh) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pbar = MnUtils.add(pbar, MnUtils.mul(simplex[i].getSecond(), wg))
|
||||||
|
}
|
||||||
|
val pstar: RealVector =
|
||||||
|
MnUtils.sub(MnUtils.mul(pbar, 1.0 + alpha), MnUtils.mul(simplex[jh].getSecond(), alpha))
|
||||||
|
val ystar: Double = mfcn.value(pstar)
|
||||||
|
if (ystar > amin) {
|
||||||
|
if (ystar < simplex[jh].getFirst()) {
|
||||||
|
simplex.update(ystar, pstar)
|
||||||
|
if (jh != simplex.jh()) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val pstst: RealVector =
|
||||||
|
MnUtils.add(MnUtils.mul(simplex[jh].getSecond(), beta), MnUtils.mul(pbar, 1.0 - beta))
|
||||||
|
val ystst: Double = mfcn.value(pstst)
|
||||||
|
if (ystst > simplex[jh].getFirst()) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
simplex.update(ystst, pstst)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var pstst: RealVector = MnUtils.add(MnUtils.mul(pstar, gamma), MnUtils.mul(pbar, 1.0 - gamma))
|
||||||
|
var ystst: Double = mfcn.value(pstst)
|
||||||
|
val y1: Double = (ystar - simplex[jh].getFirst()) * rho2
|
||||||
|
val y2: Double = (ystst - simplex[jh].getFirst()) * rho1
|
||||||
|
var rho = 0.5 * (rho2 * y1 - rho1 * y2) / (y1 - y2)
|
||||||
|
if (rho < rhomin) {
|
||||||
|
if (ystst < simplex[jl].getFirst()) {
|
||||||
|
simplex.update(ystst, pstst)
|
||||||
|
} else {
|
||||||
|
simplex.update(ystar, pstar)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (rho > rhomax) {
|
||||||
|
rho = rhomax
|
||||||
|
}
|
||||||
|
val prho: RealVector =
|
||||||
|
MnUtils.add(MnUtils.mul(pbar, rho), MnUtils.mul(simplex[jh].getSecond(), 1.0 - rho))
|
||||||
|
val yrho: Double = mfcn.value(prho)
|
||||||
|
if (yrho < simplex[jl].getFirst() && yrho < ystst) {
|
||||||
|
simplex.update(yrho, prho)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (ystst < simplex[jl].getFirst()) {
|
||||||
|
simplex.update(ystst, pstst)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (yrho > simplex[jl].getFirst()) {
|
||||||
|
if (ystst < simplex[jl].getFirst()) {
|
||||||
|
simplex.update(ystst, pstst)
|
||||||
|
} else {
|
||||||
|
simplex.update(ystar, pstar)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (ystar > simplex[jh].getFirst()) {
|
||||||
|
pstst = MnUtils.add(MnUtils.mul(simplex[jh].getSecond(), beta), MnUtils.mul(pbar, 1 - beta))
|
||||||
|
ystst = mfcn.value(pstst)
|
||||||
|
if (ystst > simplex[jh].getFirst()) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
simplex.update(ystst, pstst)
|
||||||
|
}
|
||||||
|
} while (simplex.edm() > minedm && mfcn.numOfCalls() < maxfcn)
|
||||||
|
amin = simplex[jl].getFirst()
|
||||||
|
jl = simplex.jl()
|
||||||
|
jh = simplex.jh()
|
||||||
|
var pbar: RealVector = ArrayRealVector(n)
|
||||||
|
for (i in 0 until n + 1) {
|
||||||
|
if (i == jh) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pbar = MnUtils.add(pbar, MnUtils.mul(simplex[i].getSecond(), wg))
|
||||||
|
}
|
||||||
|
var ybar: Double = mfcn.value(pbar)
|
||||||
|
if (ybar < amin) {
|
||||||
|
simplex.update(ybar, pbar)
|
||||||
|
} else {
|
||||||
|
pbar = simplex[jl].getSecond()
|
||||||
|
ybar = simplex[jl].getFirst()
|
||||||
|
}
|
||||||
|
var dirin: RealVector = simplex.dirin()
|
||||||
|
// scale to sigmas on parameters werr^2 = dirin^2 * (up/edm)
|
||||||
|
dirin = MnUtils.mul(dirin, sqrt(mfcn.errorDef() / simplex.edm()))
|
||||||
|
val st = MinimumState(MinimumParameters(pbar, dirin, ybar), simplex.edm(), mfcn.numOfCalls())
|
||||||
|
val states: MutableList<MinimumState> = java.util.ArrayList<MinimumState>(1)
|
||||||
|
states.add(st)
|
||||||
|
if (mfcn.numOfCalls() > maxfcn) {
|
||||||
|
MINUITPlugin.logStatic("Simplex did not converge, #fcn calls exhausted.")
|
||||||
|
return FunctionMinimum(seed, states, mfcn.errorDef(), MnReachedCallLimit())
|
||||||
|
}
|
||||||
|
if (simplex.edm() > minedm) {
|
||||||
|
MINUITPlugin.logStatic("Simplex did not converge, edm > minedm.")
|
||||||
|
return FunctionMinimum(seed, states, mfcn.errorDef(), MnAboveMaxEdm())
|
||||||
|
}
|
||||||
|
return FunctionMinimum(seed, states, mfcn.errorDef())
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
internal class SimplexMinimizer : ModularFunctionMinimizer() {
|
||||||
|
private val theBuilder: SimplexBuilder
|
||||||
|
private val theSeedGenerator: SimplexSeedGenerator = SimplexSeedGenerator()
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
override fun builder(): MinimumBuilder {
|
||||||
|
return theBuilder
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
override fun seedGenerator(): MinimumSeedGenerator {
|
||||||
|
return theSeedGenerator
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Constructor for SimplexMinimizer.
|
||||||
|
*/
|
||||||
|
init {
|
||||||
|
theBuilder = SimplexBuilder()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import org.apache.commons.math3.linear.ArrayRealVector
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
internal class SimplexParameters(simpl: MutableList<Pair<Double?, RealVector?>>, jh: Int, jl: Int) {
|
||||||
|
private var theJHigh: Int
|
||||||
|
private var theJLow: Int
|
||||||
|
private val theSimplexParameters: MutableList<Pair<Double?, RealVector?>>
|
||||||
|
fun dirin(): ArrayRealVector {
|
||||||
|
val dirin = ArrayRealVector(theSimplexParameters.size - 1)
|
||||||
|
for (i in 0 until theSimplexParameters.size - 1) {
|
||||||
|
var pbig: Double = theSimplexParameters[0].getSecond().getEntry(i)
|
||||||
|
var plit = pbig
|
||||||
|
for (theSimplexParameter in theSimplexParameters) {
|
||||||
|
if (theSimplexParameter.getSecond().getEntry(i) < plit) {
|
||||||
|
plit = theSimplexParameter.getSecond().getEntry(i)
|
||||||
|
}
|
||||||
|
if (theSimplexParameter.getSecond().getEntry(i) > pbig) {
|
||||||
|
pbig = theSimplexParameter.getSecond().getEntry(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dirin.setEntry(i, pbig - plit)
|
||||||
|
}
|
||||||
|
return dirin
|
||||||
|
}
|
||||||
|
|
||||||
|
fun edm(): Double {
|
||||||
|
return theSimplexParameters[jh()].getFirst() - theSimplexParameters[jl()].getFirst()
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun get(i: Int): Pair<Double?, RealVector?> {
|
||||||
|
return theSimplexParameters[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun jh(): Int {
|
||||||
|
return theJHigh
|
||||||
|
}
|
||||||
|
|
||||||
|
fun jl(): Int {
|
||||||
|
return theJLow
|
||||||
|
}
|
||||||
|
|
||||||
|
fun simplex(): List<Pair<Double?, RealVector?>> {
|
||||||
|
return theSimplexParameters
|
||||||
|
}
|
||||||
|
|
||||||
|
fun update(y: Double, p: RealVector?) {
|
||||||
|
theSimplexParameters.set(jh(), Pair(y, p))
|
||||||
|
if (y < theSimplexParameters[jl()].getFirst()) {
|
||||||
|
theJLow = jh()
|
||||||
|
}
|
||||||
|
var jh = 0
|
||||||
|
for (i in 1 until theSimplexParameters.size) {
|
||||||
|
if (theSimplexParameters[i].getFirst() > theSimplexParameters[jh].getFirst()) {
|
||||||
|
jh = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
theJHigh = jh
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
theSimplexParameters = simpl
|
||||||
|
theJHigh = jh
|
||||||
|
theJLow = jl
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import org.apache.commons.math3.linear.ArrayRealVector
|
||||||
|
import ru.inr.mass.minuit.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
internal class SimplexSeedGenerator : MinimumSeedGenerator {
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
fun generate(fcn: MnFcn, gc: GradientCalculator?, st: MnUserParameterState, stra: MnStrategy): MinimumSeed {
|
||||||
|
val n: Int = st.variableParameters()
|
||||||
|
val prec: MnMachinePrecision = st.precision()
|
||||||
|
|
||||||
|
// initial starting values
|
||||||
|
val x: RealVector = ArrayRealVector(n)
|
||||||
|
for (i in 0 until n) {
|
||||||
|
x.setEntry(i, st.intParameters()[i])
|
||||||
|
}
|
||||||
|
val fcnmin: Double = fcn.value(x)
|
||||||
|
val pa = MinimumParameters(x, fcnmin)
|
||||||
|
val igc = InitialGradientCalculator(fcn, st.getTransformation(), stra)
|
||||||
|
val dgrad: FunctionGradient = igc.gradient(pa)
|
||||||
|
val mat = MnAlgebraicSymMatrix(n)
|
||||||
|
val dcovar = 1.0
|
||||||
|
for (i in 0 until n) {
|
||||||
|
mat[i, i] = if (abs(dgrad.getGradientDerivative()
|
||||||
|
.getEntry(i)) > prec.eps2()
|
||||||
|
) 1.0 / dgrad.getGradientDerivative().getEntry(i) else 1.0
|
||||||
|
}
|
||||||
|
val err = MinimumError(mat, dcovar)
|
||||||
|
val edm: Double = VariableMetricEDMEstimator().estimate(dgrad, err)
|
||||||
|
val state = MinimumState(pa, err, dgrad, edm, fcn.numOfCalls())
|
||||||
|
return MinimumSeed(state, st.getTransformation())
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
internal class SinParameterTransformation {
|
||||||
|
fun dInt2Ext(value: Double, upper: Double, lower: Double): Double {
|
||||||
|
return 0.5 * abs((upper - lower) * cos(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ext2int(value: Double, upper: Double, lower: Double, prec: MnMachinePrecision): Double {
|
||||||
|
val piby2: Double = 2.0 * atan(1.0)
|
||||||
|
val distnn: Double = 8.0 * sqrt(prec.eps2())
|
||||||
|
val vlimhi = piby2 - distnn
|
||||||
|
val vlimlo = -piby2 + distnn
|
||||||
|
val yy = 2.0 * (value - lower) / (upper - lower) - 1.0
|
||||||
|
val yy2 = yy * yy
|
||||||
|
return if (yy2 > 1.0 - prec.eps2()) {
|
||||||
|
if (yy < 0.0) {
|
||||||
|
vlimlo
|
||||||
|
} else {
|
||||||
|
vlimhi
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
asin(yy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun int2ext(value: Double, upper: Double, lower: Double): Double {
|
||||||
|
return lower + 0.5 * (upper - lower) * (sin(value) + 1.0)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
internal class SqrtLowParameterTransformation {
|
||||||
|
// derivative of transformation from internal to external
|
||||||
|
fun dInt2Ext(value: Double, lower: Double): Double {
|
||||||
|
return value / sqrt(value * value + 1.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// transformation from external to internal
|
||||||
|
fun ext2int(value: Double, lower: Double, prec: MnMachinePrecision): Double {
|
||||||
|
val yy = value - lower + 1.0
|
||||||
|
val yy2 = yy * yy
|
||||||
|
return if (yy2 < 1.0 + prec.eps2()) {
|
||||||
|
8 * sqrt(prec.eps2())
|
||||||
|
} else {
|
||||||
|
sqrt(yy2 - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// transformation from internal to external
|
||||||
|
fun int2ext(value: Double, lower: Double): Double {
|
||||||
|
return lower - 1.0 + sqrt(value * value + 1.0)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
internal class SqrtUpParameterTransformation {
|
||||||
|
// derivative of transformation from internal to external
|
||||||
|
fun dInt2Ext(value: Double, upper: Double): Double {
|
||||||
|
return -value / sqrt(value * value + 1.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// transformation from external to internal
|
||||||
|
fun ext2int(value: Double, upper: Double, prec: MnMachinePrecision): Double {
|
||||||
|
val yy = upper - value + 1.0
|
||||||
|
val yy2 = yy * yy
|
||||||
|
return if (yy2 < 1.0 + prec.eps2()) {
|
||||||
|
8 * sqrt(prec.eps2())
|
||||||
|
} else {
|
||||||
|
sqrt(yy2 - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// transformation from internal to external
|
||||||
|
fun int2ext(value: Double, upper: Double): Double {
|
||||||
|
return upper + 1.0 - sqrt(value * value + 1.0)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,137 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
import space.kscience.kmath.optimization.minuit.MINUITPlugin
|
||||||
|
import ru.inr.mass.minuit.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
internal class VariableMetricBuilder : MinimumBuilder {
|
||||||
|
private val theErrorUpdator: DavidonErrorUpdator
|
||||||
|
private val theEstimator: VariableMetricEDMEstimator = VariableMetricEDMEstimator()
|
||||||
|
fun errorUpdator(): DavidonErrorUpdator {
|
||||||
|
return theErrorUpdator
|
||||||
|
}
|
||||||
|
|
||||||
|
fun estimator(): VariableMetricEDMEstimator {
|
||||||
|
return theEstimator
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
fun minimum(
|
||||||
|
fcn: MnFcn,
|
||||||
|
gc: GradientCalculator,
|
||||||
|
seed: MinimumSeed,
|
||||||
|
strategy: MnStrategy,
|
||||||
|
maxfcn: Int,
|
||||||
|
edmval: Double
|
||||||
|
): FunctionMinimum {
|
||||||
|
val min: FunctionMinimum = minimum(fcn, gc, seed, maxfcn, edmval)
|
||||||
|
if (strategy.strategy() === 2 || strategy.strategy() === 1 && min.error().dcovar() > 0.05) {
|
||||||
|
val st: MinimumState = MnHesse(strategy).calculate(fcn, min.state(), min.seed().trafo(), 0)
|
||||||
|
min.add(st)
|
||||||
|
}
|
||||||
|
if (!min.isValid()) {
|
||||||
|
MINUITPlugin.logStatic("FunctionMinimum is invalid.")
|
||||||
|
}
|
||||||
|
return min
|
||||||
|
}
|
||||||
|
|
||||||
|
fun minimum(fcn: MnFcn, gc: GradientCalculator, seed: MinimumSeed, maxfcn: Int, edmval: Double): FunctionMinimum {
|
||||||
|
var edmval = edmval
|
||||||
|
edmval *= 0.0001
|
||||||
|
if (seed.parameters().vec().getDimension() === 0) {
|
||||||
|
return FunctionMinimum(seed, fcn.errorDef())
|
||||||
|
}
|
||||||
|
val prec: MnMachinePrecision = seed.precision()
|
||||||
|
val result: MutableList<MinimumState> = java.util.ArrayList<MinimumState>(8)
|
||||||
|
var edm: Double = seed.state().edm()
|
||||||
|
if (edm < 0.0) {
|
||||||
|
MINUITPlugin.logStatic("VariableMetricBuilder: initial matrix not pos.def.")
|
||||||
|
if (seed.error().isPosDef()) {
|
||||||
|
throw RuntimeException("Something is wrong!")
|
||||||
|
}
|
||||||
|
return FunctionMinimum(seed, fcn.errorDef())
|
||||||
|
}
|
||||||
|
result.add(seed.state())
|
||||||
|
|
||||||
|
// iterate until edm is small enough or max # of iterations reached
|
||||||
|
edm *= 1.0 + 3.0 * seed.error().dcovar()
|
||||||
|
var step: RealVector // = new ArrayRealVector(seed.gradient().getGradient().getDimension());
|
||||||
|
do {
|
||||||
|
var s0: MinimumState = result[result.size - 1]
|
||||||
|
step = MnUtils.mul(MnUtils.mul(s0.error().invHessian(), s0.gradient().getGradient()), -1)
|
||||||
|
var gdel: Double = MnUtils.innerProduct(step, s0.gradient().getGradient())
|
||||||
|
if (gdel > 0.0) {
|
||||||
|
MINUITPlugin.logStatic("VariableMetricBuilder: matrix not pos.def.")
|
||||||
|
MINUITPlugin.logStatic("gdel > 0: $gdel")
|
||||||
|
s0 = MnPosDef.test(s0, prec)
|
||||||
|
step = MnUtils.mul(MnUtils.mul(s0.error().invHessian(), s0.gradient().getGradient()), -1)
|
||||||
|
gdel = MnUtils.innerProduct(step, s0.gradient().getGradient())
|
||||||
|
MINUITPlugin.logStatic("gdel: $gdel")
|
||||||
|
if (gdel > 0.0) {
|
||||||
|
result.add(s0)
|
||||||
|
return FunctionMinimum(seed, result, fcn.errorDef())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val pp: MnParabolaPoint = MnLineSearch.search(fcn, s0.parameters(), step, gdel, prec)
|
||||||
|
if (abs(pp.y() - s0.fval()) < prec.eps()) {
|
||||||
|
MINUITPlugin.logStatic("VariableMetricBuilder: no improvement")
|
||||||
|
break //no improvement
|
||||||
|
}
|
||||||
|
val p = MinimumParameters(MnUtils.add(s0.vec(), MnUtils.mul(step, pp.x())), pp.y())
|
||||||
|
val g: FunctionGradient = gc.gradient(p, s0.gradient())
|
||||||
|
edm = estimator().estimate(g, s0.error())
|
||||||
|
if (edm < 0.0) {
|
||||||
|
MINUITPlugin.logStatic("VariableMetricBuilder: matrix not pos.def.")
|
||||||
|
MINUITPlugin.logStatic("edm < 0")
|
||||||
|
s0 = MnPosDef.test(s0, prec)
|
||||||
|
edm = estimator().estimate(g, s0.error())
|
||||||
|
if (edm < 0.0) {
|
||||||
|
result.add(s0)
|
||||||
|
return FunctionMinimum(seed, result, fcn.errorDef())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val e: MinimumError = errorUpdator().update(s0, p, g)
|
||||||
|
result.add(MinimumState(p, e, g, edm, fcn.numOfCalls()))
|
||||||
|
// result[0] = MinimumState(p, e, g, edm, fcn.numOfCalls());
|
||||||
|
edm *= 1.0 + 3.0 * e.dcovar()
|
||||||
|
} while (edm > edmval && fcn.numOfCalls() < maxfcn)
|
||||||
|
if (fcn.numOfCalls() >= maxfcn) {
|
||||||
|
MINUITPlugin.logStatic("VariableMetricBuilder: call limit exceeded.")
|
||||||
|
return FunctionMinimum(seed, result, fcn.errorDef(), MnReachedCallLimit())
|
||||||
|
}
|
||||||
|
return if (edm > edmval) {
|
||||||
|
if (edm < abs(prec.eps2() * result[result.size - 1].fval())) {
|
||||||
|
MINUITPlugin.logStatic("VariableMetricBuilder: machine accuracy limits further improvement.")
|
||||||
|
FunctionMinimum(seed, result, fcn.errorDef())
|
||||||
|
} else if (edm < 10.0 * edmval) {
|
||||||
|
FunctionMinimum(seed, result, fcn.errorDef())
|
||||||
|
} else {
|
||||||
|
MINUITPlugin.logStatic("VariableMetricBuilder: finishes without convergence.")
|
||||||
|
MINUITPlugin.logStatic("VariableMetricBuilder: edm= $edm requested: $edmval")
|
||||||
|
FunctionMinimum(seed, result, fcn.errorDef(), MnAboveMaxEdm())
|
||||||
|
}
|
||||||
|
} else FunctionMinimum(seed, result, fcn.errorDef())
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
theErrorUpdator = DavidonErrorUpdator()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author tonyj
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
internal class VariableMetricEDMEstimator {
|
||||||
|
fun estimate(g: FunctionGradient, e: MinimumError): Double {
|
||||||
|
if (e.invHessian().size() === 1) {
|
||||||
|
return 0.5 * g.getGradient().getEntry(0) * g.getGradient().getEntry(0) * e.invHessian()[0, 0]
|
||||||
|
}
|
||||||
|
val rho: Double = MnUtils.similarity(g.getGradient(), e.invHessian())
|
||||||
|
return 0.5 * rho
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
internal class VariableMetricMinimizer : ModularFunctionMinimizer() {
|
||||||
|
private val theMinBuilder: VariableMetricBuilder
|
||||||
|
private val theMinSeedGen: MnSeedGenerator = MnSeedGenerator()
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
override fun builder(): MinimumBuilder {
|
||||||
|
return theMinBuilder
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
override fun seedGenerator(): MinimumSeedGenerator {
|
||||||
|
return theMinSeedGen
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Constructor for VariableMetricMinimizer.
|
||||||
|
*/
|
||||||
|
init {
|
||||||
|
theMinBuilder = VariableMetricBuilder()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.minuit
|
||||||
|
|
@ -0,0 +1,380 @@
|
|||||||
|
/*
|
||||||
|
* 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.qow
|
||||||
|
|
||||||
|
import space.kscience.kmath.data.ColumnarData
|
||||||
|
import space.kscience.kmath.data.XYErrorColumnarData
|
||||||
|
import space.kscience.kmath.expressions.DifferentiableExpression
|
||||||
|
import space.kscience.kmath.expressions.Expression
|
||||||
|
import space.kscience.kmath.expressions.SymbolIndexer
|
||||||
|
import space.kscience.kmath.expressions.derivative
|
||||||
|
import space.kscience.kmath.linear.*
|
||||||
|
import space.kscience.kmath.misc.Symbol
|
||||||
|
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||||
|
import space.kscience.kmath.operations.DoubleField
|
||||||
|
import space.kscience.kmath.operations.Field
|
||||||
|
import space.kscience.kmath.optimization.OptimizationFeature
|
||||||
|
import space.kscience.kmath.optimization.OptimizationProblemFactory
|
||||||
|
import space.kscience.kmath.optimization.OptimizationResult
|
||||||
|
import space.kscience.kmath.optimization.XYFit
|
||||||
|
import space.kscience.kmath.structures.DoubleBuffer
|
||||||
|
import space.kscience.kmath.structures.DoubleL2Norm
|
||||||
|
import kotlin.math.pow
|
||||||
|
|
||||||
|
|
||||||
|
private typealias ParamSet = Map<Symbol, Double>
|
||||||
|
|
||||||
|
public fun interface FitLogger {
|
||||||
|
public fun log(block: () -> String)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(UnstableKMathAPI::class)
|
||||||
|
public class QowFit(
|
||||||
|
override val symbols: List<Symbol>,
|
||||||
|
private val space: LinearSpace<Double, DoubleField>,
|
||||||
|
private val solver: LinearSolver<Double>,
|
||||||
|
) : XYFit<Double>, SymbolIndexer {
|
||||||
|
|
||||||
|
private var logger: FitLogger? = null
|
||||||
|
|
||||||
|
private var startingPoint: Map<Symbol, Double> = TODO()
|
||||||
|
private var covariance: Matrix<Double>? = TODO()
|
||||||
|
private val prior: DifferentiableExpression<Double, Expression<Double>>? = TODO()
|
||||||
|
private var data: XYErrorColumnarData<Double, Double, Double> = TODO()
|
||||||
|
private var model: DifferentiableExpression<Double, Expression<Double>> = TODO()
|
||||||
|
|
||||||
|
private val features = HashSet<OptimizationFeature>()
|
||||||
|
|
||||||
|
override fun update(result: OptimizationResult<Double>) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override val algebra: Field<Double>
|
||||||
|
get() = TODO("Not yet implemented")
|
||||||
|
|
||||||
|
override fun data(
|
||||||
|
dataSet: ColumnarData<Double>,
|
||||||
|
xSymbol: Symbol,
|
||||||
|
ySymbol: Symbol,
|
||||||
|
xErrSymbol: Symbol?,
|
||||||
|
yErrSymbol: Symbol?,
|
||||||
|
) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun model(model: (Double) -> DifferentiableExpression<Double, *>) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
private var x: Symbol = Symbol.x
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The signed distance from the model to the [i]-th point of data.
|
||||||
|
*/
|
||||||
|
private fun distance(i: Int, parameters: Map<Symbol, Double>): Double =
|
||||||
|
model(parameters + (x to data.x[i])) - data.y[i]
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The derivative of [distance]
|
||||||
|
* TODO use expressions instead
|
||||||
|
*/
|
||||||
|
private fun distanceDerivative(symbol: Symbol, i: Int, parameters: Map<Symbol, Double>): Double =
|
||||||
|
model.derivative(symbol)(parameters + (x to data.x[i]))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The dispersion of [i]-th data point
|
||||||
|
*/
|
||||||
|
private fun getDispersion(i: Int, parameters: Map<Symbol, Double>): Double = data.yErr[i].pow(2)
|
||||||
|
|
||||||
|
private fun getCovariance(weight: QoWeight): Matrix<Double> = solver.inverse(getEqDerivValues(weight))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Теоретическая ковариация весовых функций.
|
||||||
|
*
|
||||||
|
* D(\phi)=E(\phi_k(\theta_0) \phi_l(\theta_0))= disDeriv_k * disDeriv_l /sigma^2
|
||||||
|
*/
|
||||||
|
private fun covarF(weight: QoWeight): Matrix<Double> = space.buildSymmetricMatrix(symbols.size) { k, l ->
|
||||||
|
(0 until data.size).sumOf { i -> weight.derivs[k, i] * weight.derivs[l, i] / weight.dispersion[i] }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Экспериментальная ковариация весов. Формула (22) из
|
||||||
|
* http://arxiv.org/abs/physics/0604127
|
||||||
|
*
|
||||||
|
* @param source
|
||||||
|
* @param set
|
||||||
|
* @param fitPars
|
||||||
|
* @param weight
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private fun covarFExp(weight: QoWeight, theta: Map<Symbol, Double>): Matrix<Double> = space.run {
|
||||||
|
/*
|
||||||
|
* Важно! Если не делать предварителього вычисления этих производных, то
|
||||||
|
* количество вызывов функции будет dim^2 вместо dim Первый индекс -
|
||||||
|
* номер точки, второй - номер переменной, по которой берется производная
|
||||||
|
*/
|
||||||
|
val eqvalues = buildMatrix(data.size, symbols.size) { i, l ->
|
||||||
|
distance(i, theta) * weight.derivs[l, i] / weight.dispersion[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
buildMatrix(symbols.size, symbols.size) { k, l ->
|
||||||
|
(0 until data.size).sumOf { i -> eqvalues[i, l] * eqvalues[i, k] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* производные уравнений для метода Ньютона
|
||||||
|
*
|
||||||
|
* @param source
|
||||||
|
* @param set
|
||||||
|
* @param fitPars
|
||||||
|
* @param weight
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private fun getEqDerivValues(
|
||||||
|
weight: QoWeight, theta: Map<Symbol, Double> = weight.theta,
|
||||||
|
): Matrix<Double> = space.run {
|
||||||
|
val fitDim = symbols.size
|
||||||
|
//Возвращает производную k-того Eq по l-тому параметру
|
||||||
|
val res = Array(fitDim) { DoubleArray(fitDim) }
|
||||||
|
val sderiv = buildMatrix(data.size, symbols.size) { i, l ->
|
||||||
|
distanceDerivative(symbols[l], i, theta)
|
||||||
|
}
|
||||||
|
|
||||||
|
buildMatrix(symbols.size, symbols.size) { k, l ->
|
||||||
|
val base = (0 until data.size).sumOf { i ->
|
||||||
|
require(weight.dispersion[i] > 0)
|
||||||
|
sderiv[i, l] * weight.derivs[k, i] / weight.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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Значения уравнений метода квазиоптимальных весов
|
||||||
|
*
|
||||||
|
* @param source
|
||||||
|
* @param set
|
||||||
|
* @param fitPars
|
||||||
|
* @param weight
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private fun getEqValues(weight: QoWeight, theta: Map<Symbol, Double> = weight.theta): Point<Double> {
|
||||||
|
val distances = DoubleBuffer(data.size) { i -> distance(i, theta) }
|
||||||
|
|
||||||
|
return DoubleBuffer(symbols.size) { k ->
|
||||||
|
val base = (0 until data.size).sumOf { i -> distances[i] * weight.derivs[k, i] / weight.dispersion[i] }
|
||||||
|
//Поправка на априорную вероятность
|
||||||
|
prior?.let { prior ->
|
||||||
|
base - prior.derivative(symbols[k])(theta) / prior(theta)
|
||||||
|
} ?: base
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The state of QOW fitter
|
||||||
|
* Created by Alexander Nozik on 17-Oct-16.
|
||||||
|
*/
|
||||||
|
private inner class QoWeight(
|
||||||
|
val theta: Map<Symbol, Double>,
|
||||||
|
) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
require(data.size > 0) { "The state does not contain data" }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Derivatives of the spectrum over parameters. First index in the point number, second one - index of parameter
|
||||||
|
*/
|
||||||
|
val derivs: Matrix<Double> by lazy {
|
||||||
|
space.buildMatrix(data.size, symbols.size) { i, k ->
|
||||||
|
distanceDerivative(symbols[k], i, theta)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array of dispersions in each point
|
||||||
|
*/
|
||||||
|
val dispersion: Point<Double> by lazy {
|
||||||
|
DoubleBuffer(data.size) { i -> getDispersion(i, theta) }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun newtonianStep(
|
||||||
|
weight: QoWeight,
|
||||||
|
par: Map<Symbol, Double>,
|
||||||
|
eqvalues: Point<Double>,
|
||||||
|
): Map<Symbol, Double> = space.run {
|
||||||
|
val start = par.toPoint()
|
||||||
|
val invJacob = solver.inverse(getEqDerivValues(weight, par))
|
||||||
|
|
||||||
|
val step = invJacob.dot(eqvalues)
|
||||||
|
return par + (start - step).toMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun newtonianRun(
|
||||||
|
weight: QoWeight,
|
||||||
|
maxSteps: Int = 100,
|
||||||
|
tolerance: Double = 0.0,
|
||||||
|
fast: Boolean = false,
|
||||||
|
): ParamSet {
|
||||||
|
|
||||||
|
var dis: Double//норма невязки
|
||||||
|
// Для удобства работаем всегда с полным набором параметров
|
||||||
|
var par = startingPoint
|
||||||
|
|
||||||
|
logger?.log { "Starting newtonian iteration from: \n\t$par" }
|
||||||
|
|
||||||
|
var eqvalues = getEqValues(weight, 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(weight, weight.theta, eqvalues)
|
||||||
|
} else {
|
||||||
|
//Берет значения матрицы в точке par
|
||||||
|
newtonianStep(weight, par, eqvalues)
|
||||||
|
}
|
||||||
|
// здесь должен стоять учет границ параметров
|
||||||
|
logger?.log { "Parameter values after step are: \n\t$currentSolution" }
|
||||||
|
|
||||||
|
eqvalues = getEqValues(weight, 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 par
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// override fun run(state: FitState, parentLog: History?, meta: Meta): FitResult {
|
||||||
|
// val log = Chronicle("QOW", parentLog)
|
||||||
|
// val action = meta.getString(FIT_STAGE_TYPE, TASK_RUN)
|
||||||
|
// log.report("QOW fit engine started task '{}'", action)
|
||||||
|
// return when (action) {
|
||||||
|
// TASK_SINGLE -> makeRun(state, log, meta)
|
||||||
|
// TASK_COVARIANCE -> generateErrors(state, log, meta)
|
||||||
|
// TASK_RUN -> {
|
||||||
|
// var res = makeRun(state, log, meta)
|
||||||
|
// res = makeRun(res.optState().get(), log, meta)
|
||||||
|
// generateErrors(res.optState().get(), log, meta)
|
||||||
|
// }
|
||||||
|
// else -> throw IllegalArgumentException("Unknown task")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// private fun makeRun(state: FitState, log: History, meta: Meta): FitResult {
|
||||||
|
// /*Инициализация объектов, задание исходных значений*/
|
||||||
|
// log.report("Starting fit using quasioptimal weights method.")
|
||||||
|
//
|
||||||
|
// val fitPars = getFitPars(state, meta)
|
||||||
|
//
|
||||||
|
// val curWeight = QoWeight(state, fitPars, state.parameters)
|
||||||
|
//
|
||||||
|
// // вычисляем вес в allPar. Потом можно будет попробовать ручное задание веса
|
||||||
|
// log.report("The starting weight is: \n\t{}",
|
||||||
|
// MathUtils.toString(curWeight.theta))
|
||||||
|
//
|
||||||
|
// //Стартовая точка такая же как и параметр веса
|
||||||
|
// /*Фитирование*/
|
||||||
|
// val res = newtonianRun(state, curWeight, log, meta)
|
||||||
|
//
|
||||||
|
// /*Генерация результата*/
|
||||||
|
//
|
||||||
|
// return FitResult.build(state.edit().setPars(res).build(), *fitPars)
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* generateErrors.
|
||||||
|
*/
|
||||||
|
private fun generateErrors(): Matrix<Double> {
|
||||||
|
logger?.log { """
|
||||||
|
Starting errors estimation using quasioptimal weights method. The starting weight is:
|
||||||
|
${curWeight.theta}
|
||||||
|
""".trimIndent()}
|
||||||
|
val curWeight = QoWeight(startingPoint)
|
||||||
|
|
||||||
|
val covar = getCovariance(curWeight)
|
||||||
|
|
||||||
|
val decomposition = EigenDecomposition(covar.matrix)
|
||||||
|
var valid = true
|
||||||
|
for (lambda in decomposition.realEigenvalues) {
|
||||||
|
if (lambda <= 0) {
|
||||||
|
log.report("The covariance matrix is not positive defined. Error estimation is not valid")
|
||||||
|
valid = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override suspend fun optimize(): OptimizationResult<Double> {
|
||||||
|
val curWeight = QoWeight(startingPoint)
|
||||||
|
logger?.log {
|
||||||
|
"""
|
||||||
|
Starting fit using quasioptimal weights method. The starting weight is:
|
||||||
|
${curWeight.theta}
|
||||||
|
""".trimIndent()
|
||||||
|
}
|
||||||
|
val res = newtonianRun(curWeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
companion object : OptimizationProblemFactory<Double, QowFit> {
|
||||||
|
override fun build(symbols: List<Symbol>): QowFit {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant `QOW_ENGINE_NAME="QOW"`
|
||||||
|
*/
|
||||||
|
const val QOW_ENGINE_NAME = "QOW"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant `QOW_METHOD_FAST="fast"`
|
||||||
|
*/
|
||||||
|
const val QOW_METHOD_FAST = "fast"
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user