[WIP] Refactor optimization
This commit is contained in:
parent
2cf56641aa
commit
257337f4fb
@ -5,7 +5,7 @@
|
||||
|
||||
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.operations.DoubleField
|
||||
import kotlin.math.pow
|
||||
@ -15,7 +15,7 @@ fun main() {
|
||||
val function: UnivariateFunction<Double> = { x -> 3 * x.pow(2) + 2 * x + 1 }
|
||||
|
||||
//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
|
||||
println(result.value)
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
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.nd.StructureND
|
||||
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 }
|
||||
|
||||
//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
|
||||
println(result.value)
|
||||
|
@ -24,7 +24,7 @@ public class CMIntegrator(
|
||||
public class MinIterations(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 maxCalls = integrand.getFeature<IntegrandMaxCalls>()?.maxCalls ?: defaultMaxCalls
|
||||
val remainingCalls = maxCalls - integrand.calls
|
||||
@ -32,11 +32,12 @@ public class CMIntegrator(
|
||||
?: error("Integration range is not provided")
|
||||
val res = integrator.integrate(remainingCalls, integrand.function, range.start, range.endInclusive)
|
||||
|
||||
return integrand +
|
||||
IntegrandValue(res) +
|
||||
IntegrandAbsoluteAccuracy(integrator.absoluteAccuracy) +
|
||||
IntegrandRelativeAccuracy(integrator.relativeAccuracy) +
|
||||
IntegrandCallsPerformed(integrator.evaluations + integrand.calls)
|
||||
return integrand.with(
|
||||
IntegrandValue(res),
|
||||
IntegrandAbsoluteAccuracy(integrator.absoluteAccuracy),
|
||||
IntegrandRelativeAccuracy(integrator.relativeAccuracy),
|
||||
IntegrandCallsPerformed(integrator.evaluations + integrand.calls)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
@ -16,7 +16,7 @@ public class GaussRuleIntegrator(
|
||||
private var type: GaussRule = GaussRule.LEGANDRE,
|
||||
) : UnivariateIntegrator<Double> {
|
||||
|
||||
override fun integrate(integrand: UnivariateIntegrand<Double>): UnivariateIntegrand<Double> {
|
||||
override fun process(integrand: UnivariateIntegrand<Double>): UnivariateIntegrand<Double> {
|
||||
val range = integrand.getFeature<IntegrationRange>()?.range
|
||||
?: error("Integration range is not provided")
|
||||
val integrator: GaussIntegrator = getIntegrator(range)
|
||||
@ -76,7 +76,7 @@ public class GaussRuleIntegrator(
|
||||
numPoints: Int = 100,
|
||||
type: GaussRule = GaussRule.LEGANDRE,
|
||||
function: (Double) -> Double,
|
||||
): Double = GaussRuleIntegrator(numPoints, type).integrate(
|
||||
): Double = GaussRuleIntegrator(numPoints, type).process(
|
||||
UnivariateIntegrand(function, IntegrationRange(range))
|
||||
).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.NelderMeadSimplex
|
||||
import org.apache.commons.math3.optim.nonlinear.scalar.noderiv.SimplexOptimizer
|
||||
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.expressions.*
|
||||
import space.kscience.kmath.misc.Symbol
|
||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||
import space.kscience.kmath.optimization.*
|
||||
@ -26,94 +23,98 @@ import kotlin.reflect.KClass
|
||||
public operator fun PointValuePair.component1(): DoubleArray = point
|
||||
public operator fun PointValuePair.component2(): Double = value
|
||||
|
||||
public class CMOptimizerFactory(public val optimizerBuilder: () -> MultivariateOptimizer) : OptimizationFeature
|
||||
//public class CMOptimizerData(public val )
|
||||
|
||||
@OptIn(UnstableKMathAPI::class)
|
||||
public class CMOptimization(
|
||||
override val symbols: List<Symbol>,
|
||||
) : FunctionOptimization<Double>, NoDerivFunctionOptimization<Double>, SymbolIndexer, OptimizationFeature {
|
||||
public class CMOptimization : Optimizer<FunctionOptimization<Double>> {
|
||||
|
||||
private val optimizationData: HashMap<KClass<out OptimizationData>, OptimizationData> = HashMap()
|
||||
private var optimizerBuilder: (() -> MultivariateOptimizer)? = null
|
||||
public var convergenceChecker: ConvergenceChecker<PointValuePair> = SimpleValueChecker(
|
||||
DEFAULT_RELATIVE_TOLERANCE,
|
||||
DEFAULT_ABSOLUTE_TOLERANCE,
|
||||
DEFAULT_MAX_ITER
|
||||
)
|
||||
override suspend fun process(
|
||||
problem: FunctionOptimization<Double>
|
||||
): FunctionOptimization<Double> = withSymbols(problem.parameters){
|
||||
val cmOptimizer: MultivariateOptimizer =
|
||||
problem.getFeature<CMOptimizerFactory>()?.optimizerBuilder?.invoke() ?: SimplexOptimizer()
|
||||
|
||||
override var maximize: Boolean
|
||||
get() = optimizationData[GoalType::class] == GoalType.MAXIMIZE
|
||||
set(value) {
|
||||
optimizationData[GoalType::class] = if (value) GoalType.MAXIMIZE else GoalType.MINIMIZE
|
||||
val convergenceChecker: ConvergenceChecker<PointValuePair> = SimpleValueChecker(
|
||||
DEFAULT_RELATIVE_TOLERANCE,
|
||||
DEFAULT_ABSOLUTE_TOLERANCE,
|
||||
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(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 {
|
||||
val args = it.toMap()
|
||||
expression(args)
|
||||
/**
|
||||
* Register no-deriv function instead of differentiable function
|
||||
*/
|
||||
/**
|
||||
* 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>>) {
|
||||
function(expression)
|
||||
val gradientFunction = ObjectiveFunctionGradient {
|
||||
val args = it.toMap()
|
||||
DoubleArray(symbols.size) { index ->
|
||||
expression.derivative(symbols[index])(args)
|
||||
public override fun function(expression: DifferentiableExpression<Double, Expression<Double>>) {
|
||||
noDerivFunction(expression)
|
||||
val gradientFunction = ObjectiveFunctionGradient {
|
||||
val args = startingPoint + it.toMap()
|
||||
DoubleArray(symbols.size) { index ->
|
||||
expression.derivative(symbols[index])(args)
|
||||
}
|
||||
}
|
||||
addOptimizationData(gradientFunction)
|
||||
if (optimizerBuilder == null) {
|
||||
optimizerBuilder = {
|
||||
NonLinearConjugateGradientOptimizer(
|
||||
NonLinearConjugateGradientOptimizer.Formula.FLETCHER_REEVES,
|
||||
convergenceChecker
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
addOptimizationData(gradientFunction)
|
||||
if (optimizerBuilder == null) {
|
||||
optimizerBuilder = {
|
||||
NonLinearConjugateGradientOptimizer(
|
||||
NonLinearConjugateGradientOptimizer.Formula.FLETCHER_REEVES,
|
||||
convergenceChecker
|
||||
)
|
||||
|
||||
public fun simplex(simplex: AbstractSimplex) {
|
||||
addOptimizationData(simplex)
|
||||
//Set optimization builder to simplex if it is not present
|
||||
if (optimizerBuilder == null) {
|
||||
optimizerBuilder = { SimplexOptimizer(convergenceChecker) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public fun simplex(simplex: AbstractSimplex) {
|
||||
addOptimizationData(simplex)
|
||||
//Set optimization builder to simplex if it is not present
|
||||
if (optimizerBuilder == null) {
|
||||
optimizerBuilder = { SimplexOptimizer(convergenceChecker) }
|
||||
public fun simplexSteps(steps: Map<Symbol, Double>) {
|
||||
simplex(NelderMeadSimplex(steps.toDoubleArray()))
|
||||
}
|
||||
}
|
||||
|
||||
public fun simplexSteps(steps: Map<Symbol, Double>) {
|
||||
simplex(NelderMeadSimplex(steps.toDoubleArray()))
|
||||
}
|
||||
public fun goal(goalType: GoalType) {
|
||||
addOptimizationData(goalType)
|
||||
}
|
||||
|
||||
public fun goal(goalType: GoalType) {
|
||||
addOptimizationData(goalType)
|
||||
}
|
||||
public fun optimizer(block: () -> MultivariateOptimizer) {
|
||||
optimizerBuilder = block
|
||||
}
|
||||
|
||||
public fun optimizer(block: () -> MultivariateOptimizer) {
|
||||
optimizerBuilder = block
|
||||
}
|
||||
override fun update(result: OptimizationResult<Double>) {
|
||||
initialGuess(result.point)
|
||||
}
|
||||
|
||||
override fun update(result: OptimizationResult<Double>) {
|
||||
initialGuess(result.point)
|
||||
}
|
||||
|
||||
override fun optimize(): OptimizationResult<Double> {
|
||||
val optimizer = optimizerBuilder?.invoke() ?: error("Optimizer not defined")
|
||||
val (point, value) = optimizer.optimize(*optimizationData.values.toTypedArray())
|
||||
return OptimizationResult(point.toMap(), value, setOf(this))
|
||||
override suspend fun optimize(): OptimizationResult<Double> {
|
||||
val optimizer = optimizerBuilder?.invoke() ?: error("Optimizer not defined")
|
||||
val (point, value) = optimizer.optimize(*optimizationData.values.toTypedArray())
|
||||
return OptimizationResult(point.toMap(), value)
|
||||
}
|
||||
return@withSymbols TODO()
|
||||
}
|
||||
|
||||
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.Expression
|
||||
import space.kscience.kmath.misc.Symbol
|
||||
import space.kscience.kmath.optimization.FunctionOptimization
|
||||
import space.kscience.kmath.optimization.OptimizationResult
|
||||
import space.kscience.kmath.optimization.noDerivOptimizeWith
|
||||
import space.kscience.kmath.optimization.optimizeWith
|
||||
import space.kscience.kmath.optimization.*
|
||||
import space.kscience.kmath.structures.Buffer
|
||||
import space.kscience.kmath.structures.asBuffer
|
||||
|
||||
@ -46,20 +43,25 @@ public fun FunctionOptimization.Companion.chiSquared(
|
||||
/**
|
||||
* Optimize expression without derivatives
|
||||
*/
|
||||
public fun Expression<Double>.optimize(
|
||||
public suspend fun Expression<Double>.optimize(
|
||||
vararg symbols: Symbol,
|
||||
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
|
||||
*/
|
||||
public fun DifferentiableExpression<Double, Expression<Double>>.optimize(
|
||||
public suspend fun DifferentiableExpression<Double, Expression<Double>>.optimize(
|
||||
vararg symbols: Symbol,
|
||||
configuration: CMOptimization.() -> Unit,
|
||||
): 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>,
|
||||
configuration: CMOptimization.() -> Unit = {},
|
||||
): OptimizationResult<Double> {
|
||||
@ -67,7 +69,7 @@ public fun DifferentiableExpression<Double, Expression<Double>>.minimize(
|
||||
return optimize(*symbols){
|
||||
maximize = false
|
||||
initialGuess(startPoint.toMap())
|
||||
diffFunction(this@minimize)
|
||||
function(this@minimize)
|
||||
configuration()
|
||||
}
|
||||
}
|
@ -24,7 +24,7 @@ internal class OptimizeTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGradientOptimization() {
|
||||
fun testGradientOptimization() = runBlocking{
|
||||
val result = normal.optimize(x, y) {
|
||||
initialGuess(x to 1.0, y to 1.0)
|
||||
//no need to select optimizer. Gradient optimizer is used by default because gradients are provided by function
|
||||
@ -34,7 +34,7 @@ internal class OptimizeTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSimplexOptimization() {
|
||||
fun testSimplexOptimization() = runBlocking{
|
||||
val result = normal.optimize(x, y) {
|
||||
initialGuess(x to 1.0, y to 1.0)
|
||||
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
|
||||
|
||||
/**
|
||||
* 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].
|
||||
*/
|
||||
@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]
|
||||
*/
|
||||
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>
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import space.kscience.kmath.misc.Symbol
|
||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||
import space.kscience.kmath.nd.Structure2D
|
||||
import space.kscience.kmath.structures.BufferFactory
|
||||
import space.kscience.kmath.structures.DoubleBuffer
|
||||
import kotlin.jvm.JvmInline
|
||||
|
||||
/**
|
||||
@ -46,6 +47,11 @@ public interface SymbolIndexer {
|
||||
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 =
|
||||
get(indexOf(rowSymbol), indexOf(columnSymbol))
|
||||
|
||||
@ -55,6 +61,10 @@ public interface SymbolIndexer {
|
||||
public fun <T> Map<Symbol, T>.toPoint(bufferFactory: BufferFactory<T>): Point<T> =
|
||||
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]) }
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
*/
|
||||
internal class BufferAccessor2D<T : Any>(
|
||||
internal class BufferAccessor2D<T>(
|
||||
public val rowNum: Int,
|
||||
public val colNum: Int,
|
||||
val factory: MutableBufferFactory<T>,
|
||||
|
@ -5,8 +5,10 @@
|
||||
|
||||
package space.kscience.kmath.structures
|
||||
|
||||
import space.kscience.kmath.linear.Point
|
||||
import space.kscience.kmath.operations.ExtendedField
|
||||
import space.kscience.kmath.operations.ExtendedFieldOperations
|
||||
import space.kscience.kmath.operations.Norm
|
||||
import kotlin.math.*
|
||||
|
||||
/**
|
||||
@ -161,12 +163,16 @@ public object DoubleBufferFieldOperations : ExtendedFieldOperations<Buffer<Doubl
|
||||
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].
|
||||
*
|
||||
* @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 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" }
|
||||
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.misc.UnstableKMathAPI
|
||||
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.asBuffer
|
||||
import space.kscience.kmath.structures.fold
|
||||
import space.kscience.kmath.structures.indices
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sqrt
|
||||
|
||||
@ -105,8 +102,4 @@ public fun DoubleVector.sum(): Double {
|
||||
return res
|
||||
}
|
||||
|
||||
public object VectorL2Norm : Norm<DoubleVector, Double> {
|
||||
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)
|
||||
public val DoubleVector.norm: Double get() = DoubleL2Norm.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 (points, weights) = buildRule(integrand)
|
||||
var res = zero
|
||||
@ -63,7 +63,7 @@ public class GaussIntegrator<T : Any>(
|
||||
c = t - res - y
|
||||
res = t
|
||||
}
|
||||
return integrand + IntegrandValue(res) + IntegrandCallsPerformed(integrand.calls + points.size)
|
||||
return integrand.with(IntegrandValue(res),IntegrandCallsPerformed(integrand.calls + points.size))
|
||||
}
|
||||
|
||||
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]
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public fun <T : Any> Field<T>.integrate(
|
||||
public fun <T : Any> Field<T>.process(
|
||||
vararg features: IntegrandFeature,
|
||||
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]
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public fun <T : Any> Field<T>.integrate(
|
||||
public fun <T : Any> Field<T>.process(
|
||||
range: ClosedRange<Double>,
|
||||
order: Int = 10,
|
||||
intervals: Int = 10,
|
||||
@ -104,7 +104,7 @@ public fun <T : Any> Field<T>.integrate(
|
||||
val ranges = UnivariateIntegrandRanges(
|
||||
(0 until intervals).map { i -> (rangeSize * i)..(rangeSize * (i + 1)) to order }
|
||||
)
|
||||
return GaussIntegrator(this).integrate(
|
||||
return GaussIntegrator(this).process(
|
||||
UnivariateIntegrand(
|
||||
function,
|
||||
IntegrationRange(range),
|
||||
|
@ -5,12 +5,15 @@
|
||||
|
||||
package space.kscience.kmath.integration
|
||||
|
||||
import space.kscience.kmath.misc.FeatureSet
|
||||
import space.kscience.kmath.misc.Featured
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
public interface IntegrandFeature
|
||||
|
||||
public interface Integrand {
|
||||
public fun <T : IntegrandFeature> getFeature(type: KClass<T>): T?
|
||||
public interface Integrand: Featured<IntegrandFeature>{
|
||||
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)
|
||||
|
@ -12,5 +12,5 @@ public interface Integrator<I : Integrand> {
|
||||
/**
|
||||
* 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
|
||||
|
||||
import space.kscience.kmath.linear.Point
|
||||
import space.kscience.kmath.misc.FeatureSet
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
public class MultivariateIntegrand<T : Any> internal constructor(
|
||||
private val features: Map<KClass<*>, IntegrandFeature>,
|
||||
override val features: FeatureSet<IntegrandFeature>,
|
||||
public val function: (Point<T>) -> T,
|
||||
) : Integrand {
|
||||
) : Integrand
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : IntegrandFeature> getFeature(type: KClass<T>): T? = features[type] as? T
|
||||
|
||||
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)
|
||||
}
|
||||
public fun <T : Any> MultivariateIntegrand<T>.with(vararg newFeatures: IntegrandFeature): MultivariateIntegrand<T> =
|
||||
MultivariateIntegrand(features.with(*newFeatures), function)
|
||||
|
||||
@Suppress("FunctionName")
|
||||
public fun <T : Any> MultivariateIntegrand(
|
||||
vararg features: IntegrandFeature,
|
||||
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
|
||||
|
@ -5,30 +5,24 @@
|
||||
|
||||
package space.kscience.kmath.integration
|
||||
|
||||
import space.kscience.kmath.misc.FeatureSet
|
||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||
import kotlin.jvm.JvmInline
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
|
||||
public class UnivariateIntegrand<T : Any> internal constructor(
|
||||
private val features: Map<KClass<*>, IntegrandFeature>,
|
||||
override val features: FeatureSet<IntegrandFeature>,
|
||||
public val function: (Double) -> T,
|
||||
) : Integrand {
|
||||
) : Integrand
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : IntegrandFeature> getFeature(type: KClass<T>): T? = features[type] as? T
|
||||
|
||||
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)
|
||||
}
|
||||
public fun <T : Any> UnivariateIntegrand<T>.with(vararg newFeatures: IntegrandFeature): UnivariateIntegrand<T> =
|
||||
UnivariateIntegrand(features.with(*newFeatures), function)
|
||||
|
||||
@Suppress("FunctionName")
|
||||
public fun <T : Any> UnivariateIntegrand(
|
||||
function: (Double) -> T,
|
||||
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>>
|
||||
|
||||
@ -46,7 +40,7 @@ public fun UnivariateIntegrator<Double>.integrate(
|
||||
range: ClosedRange<Double>,
|
||||
vararg features: IntegrandFeature,
|
||||
function: (Double) -> Double,
|
||||
): Double = integrate(
|
||||
): Double = process(
|
||||
UnivariateIntegrand(function, IntegrationRange(range), *features)
|
||||
).value ?: error("Unexpected: no value after integration.")
|
||||
|
||||
@ -65,7 +59,7 @@ public fun UnivariateIntegrator<Double>.integrate(
|
||||
featureBuilder()
|
||||
add(IntegrationRange(range))
|
||||
}
|
||||
return integrate(
|
||||
return process(
|
||||
UnivariateIntegrand(function, *features.toTypedArray())
|
||||
).value ?: error("Unexpected: no value after integration.")
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ import kotlin.test.assertEquals
|
||||
class GaussIntegralTest {
|
||||
@Test
|
||||
fun gaussSin() {
|
||||
val res = DoubleField.integrate(0.0..2 * PI) { x ->
|
||||
val res = DoubleField.process(0.0..2 * PI) { x ->
|
||||
sin(x)
|
||||
}
|
||||
assertEquals(0.0, res.value!!, 1e-2)
|
||||
@ -24,7 +24,7 @@ class GaussIntegralTest {
|
||||
|
||||
@Test
|
||||
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){
|
||||
1.0
|
||||
} else {
|
||||
|
@ -9,86 +9,97 @@ import space.kscience.kmath.expressions.AutoDiffProcessor
|
||||
import space.kscience.kmath.expressions.DifferentiableExpression
|
||||
import space.kscience.kmath.expressions.Expression
|
||||
import space.kscience.kmath.expressions.ExpressionAlgebra
|
||||
import space.kscience.kmath.misc.FeatureSet
|
||||
import space.kscience.kmath.misc.Symbol
|
||||
import space.kscience.kmath.operations.ExtendedField
|
||||
import space.kscience.kmath.structures.Buffer
|
||||
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
|
||||
|
||||
/**
|
||||
* Define the initial guess for the optimization problem
|
||||
*/
|
||||
public fun initialGuess(map: Map<Symbol, T>)
|
||||
public class FunctionOptimization<T : Any>(
|
||||
override val features: FeatureSet<OptimizationFeature>,
|
||||
public val expression: DifferentiableExpression<T, Expression<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
|
||||
*/
|
||||
public fun diffFunction(expression: DifferentiableExpression<T, Expression<T>>)
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* Generate a chi squared expression from given x-y-sigma data and inline model. Provides automatic differentiation
|
||||
*/
|
||||
public fun <T : Any, I : Any, A> chiSquared(
|
||||
autoDiff: AutoDiffProcessor<T, I, A, Expression<T>>,
|
||||
x: Buffer<T>,
|
||||
y: Buffer<T>,
|
||||
yErr: Buffer<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" }
|
||||
require(y.size == yErr.size) { "Y and yErr buffer should of the same size" }
|
||||
|
||||
return autoDiff.process {
|
||||
var sum = zero
|
||||
|
||||
x.indices.forEach {
|
||||
val xValue = const(x[it])
|
||||
val yValue = const(y[it])
|
||||
val yErrValue = const(yErr[it])
|
||||
val modelValue = model(xValue)
|
||||
sum += ((yValue - modelValue) / yErrValue).pow(2)
|
||||
}
|
||||
|
||||
sum
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a chi-squared-based objective function
|
||||
*/
|
||||
public fun <T: Any, I : Any, A> FunctionOptimization<T>.chiSquared(
|
||||
autoDiff: AutoDiffProcessor<T, I, A, Expression<T>>,
|
||||
x: Buffer<T>,
|
||||
y: Buffer<T>,
|
||||
yErr: Buffer<T>,
|
||||
model: A.(I) -> I,
|
||||
) where A : ExtendedField<I>, A : ExpressionAlgebra<T, I> {
|
||||
val chiSquared = FunctionOptimization.chiSquared(autoDiff, x, y, yErr, model)
|
||||
diffFunction(chiSquared)
|
||||
maximize = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimize differentiable expression using specific [OptimizationProblemFactory]
|
||||
*/
|
||||
public 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.diffFunction(this)
|
||||
return problem.optimize()
|
||||
}
|
||||
//
|
||||
///**
|
||||
// * A likelihood function optimization problem with provided derivatives
|
||||
// */
|
||||
//public interface FunctionOptimizationBuilder<T : Any> {
|
||||
// /**
|
||||
// * 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 a differentiable expression as objective function as function and gradient provider
|
||||
// */
|
||||
// public fun function(expression: DifferentiableExpression<T, Expression<T>>)
|
||||
//
|
||||
// public companion object {
|
||||
// /**
|
||||
// * Generate a chi squared expression from given x-y-sigma data and inline model. Provides automatic differentiation
|
||||
// */
|
||||
// public fun <T : Any, I : Any, A> chiSquared(
|
||||
// autoDiff: AutoDiffProcessor<T, I, A, Expression<T>>,
|
||||
// x: Buffer<T>,
|
||||
// y: Buffer<T>,
|
||||
// yErr: Buffer<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" }
|
||||
// require(y.size == yErr.size) { "Y and yErr buffer should of the same size" }
|
||||
//
|
||||
// return autoDiff.process {
|
||||
// var sum = zero
|
||||
//
|
||||
// x.indices.forEach {
|
||||
// val xValue = const(x[it])
|
||||
// val yValue = const(y[it])
|
||||
// val yErrValue = const(yErr[it])
|
||||
// val modelValue = model(xValue)
|
||||
// sum += ((yValue - modelValue) / yErrValue).pow(2)
|
||||
// }
|
||||
//
|
||||
// sum
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
///**
|
||||
// * Define a chi-squared-based objective function
|
||||
// */
|
||||
//public fun <T : Any, I : Any, A> FunctionOptimization<T>.chiSquared(
|
||||
// autoDiff: AutoDiffProcessor<T, I, A, Expression<T>>,
|
||||
// x: Buffer<T>,
|
||||
// y: Buffer<T>,
|
||||
// yErr: Buffer<T>,
|
||||
// model: A.(I) -> I,
|
||||
//) where A : ExtendedField<I>, A : ExpressionAlgebra<T, I> {
|
||||
// val chiSquared = FunctionOptimization.chiSquared(autoDiff, x, y, yErr, model)
|
||||
// function(chiSquared)
|
||||
// maximize = false
|
||||
//}
|
||||
//
|
||||
///**
|
||||
// * 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
|
||||
|
||||
@UnstableKMathAPI
|
||||
public interface XYFit<T : Any> : Optimization<T> {
|
||||
public interface XYFit<T> : OptimizationProblem<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