[WIP] Refactor optimization

This commit is contained in:
Alexander Nozik 2021-04-25 22:34:59 +03:00
parent 2cf56641aa
commit 257337f4fb
100 changed files with 11509 additions and 346 deletions

View File

@ -5,7 +5,7 @@
package space.kscience.kmath.functions package space.kscience.kmath.functions
import space.kscience.kmath.integration.integrate import space.kscience.kmath.integration.process
import space.kscience.kmath.integration.value import space.kscience.kmath.integration.value
import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.DoubleField
import kotlin.math.pow import kotlin.math.pow
@ -15,7 +15,7 @@ fun main() {
val function: UnivariateFunction<Double> = { x -> 3 * x.pow(2) + 2 * x + 1 } val function: UnivariateFunction<Double> = { x -> 3 * x.pow(2) + 2 * x + 1 }
//get the result of the integration //get the result of the integration
val result = DoubleField.integrate(0.0..10.0, function = function) val result = DoubleField.process(0.0..10.0, function = function)
//the value is nullable because in some cases the integration could not succeed //the value is nullable because in some cases the integration could not succeed
println(result.value) println(result.value)

View File

@ -5,7 +5,7 @@
package space.kscience.kmath.functions package space.kscience.kmath.functions
import space.kscience.kmath.integration.integrate import space.kscience.kmath.integration.process
import space.kscience.kmath.integration.value import space.kscience.kmath.integration.value
import space.kscience.kmath.nd.StructureND import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.nd.nd import space.kscience.kmath.nd.nd
@ -24,7 +24,7 @@ fun main(): Unit = DoubleField {
val function: (Double) -> StructureND<Double> = { x: Double -> 3 * number(x).pow(2) + 2 * diagonal(x) + 1 } val function: (Double) -> StructureND<Double> = { x: Double -> 3 * number(x).pow(2) + 2 * diagonal(x) + 1 }
//get the result of the integration //get the result of the integration
val result = integrate(0.0..10.0, function = function) val result = process(0.0..10.0, function = function)
//the value is nullable because in some cases the integration could not succeed //the value is nullable because in some cases the integration could not succeed
println(result.value) println(result.value)

View File

@ -24,7 +24,7 @@ public class CMIntegrator(
public class MinIterations(public val value: Int) : IntegrandFeature public class MinIterations(public val value: Int) : IntegrandFeature
public class MaxIterations(public val value: Int) : IntegrandFeature public class MaxIterations(public val value: Int) : IntegrandFeature
override fun integrate(integrand: UnivariateIntegrand<Double>): UnivariateIntegrand<Double> { override fun process(integrand: UnivariateIntegrand<Double>): UnivariateIntegrand<Double> {
val integrator = integratorBuilder(integrand) val integrator = integratorBuilder(integrand)
val maxCalls = integrand.getFeature<IntegrandMaxCalls>()?.maxCalls ?: defaultMaxCalls val maxCalls = integrand.getFeature<IntegrandMaxCalls>()?.maxCalls ?: defaultMaxCalls
val remainingCalls = maxCalls - integrand.calls val remainingCalls = maxCalls - integrand.calls
@ -32,11 +32,12 @@ public class CMIntegrator(
?: error("Integration range is not provided") ?: error("Integration range is not provided")
val res = integrator.integrate(remainingCalls, integrand.function, range.start, range.endInclusive) val res = integrator.integrate(remainingCalls, integrand.function, range.start, range.endInclusive)
return integrand + return integrand.with(
IntegrandValue(res) + IntegrandValue(res),
IntegrandAbsoluteAccuracy(integrator.absoluteAccuracy) + IntegrandAbsoluteAccuracy(integrator.absoluteAccuracy),
IntegrandRelativeAccuracy(integrator.relativeAccuracy) + IntegrandRelativeAccuracy(integrator.relativeAccuracy),
IntegrandCallsPerformed(integrator.evaluations + integrand.calls) IntegrandCallsPerformed(integrator.evaluations + integrand.calls)
)
} }

View File

@ -16,7 +16,7 @@ public class GaussRuleIntegrator(
private var type: GaussRule = GaussRule.LEGANDRE, private var type: GaussRule = GaussRule.LEGANDRE,
) : UnivariateIntegrator<Double> { ) : UnivariateIntegrator<Double> {
override fun integrate(integrand: UnivariateIntegrand<Double>): UnivariateIntegrand<Double> { override fun process(integrand: UnivariateIntegrand<Double>): UnivariateIntegrand<Double> {
val range = integrand.getFeature<IntegrationRange>()?.range val range = integrand.getFeature<IntegrationRange>()?.range
?: error("Integration range is not provided") ?: error("Integration range is not provided")
val integrator: GaussIntegrator = getIntegrator(range) val integrator: GaussIntegrator = getIntegrator(range)
@ -76,7 +76,7 @@ public class GaussRuleIntegrator(
numPoints: Int = 100, numPoints: Int = 100,
type: GaussRule = GaussRule.LEGANDRE, type: GaussRule = GaussRule.LEGANDRE,
function: (Double) -> Double, function: (Double) -> Double,
): Double = GaussRuleIntegrator(numPoints, type).integrate( ): Double = GaussRuleIntegrator(numPoints, type).process(
UnivariateIntegrand(function, IntegrationRange(range)) UnivariateIntegrand(function, IntegrationRange(range))
).value!! ).value!!
} }

View File

@ -14,10 +14,7 @@ import org.apache.commons.math3.optim.nonlinear.scalar.gradient.NonLinearConjuga
import org.apache.commons.math3.optim.nonlinear.scalar.noderiv.AbstractSimplex import org.apache.commons.math3.optim.nonlinear.scalar.noderiv.AbstractSimplex
import org.apache.commons.math3.optim.nonlinear.scalar.noderiv.NelderMeadSimplex import org.apache.commons.math3.optim.nonlinear.scalar.noderiv.NelderMeadSimplex
import org.apache.commons.math3.optim.nonlinear.scalar.noderiv.SimplexOptimizer import org.apache.commons.math3.optim.nonlinear.scalar.noderiv.SimplexOptimizer
import space.kscience.kmath.expressions.DifferentiableExpression import space.kscience.kmath.expressions.*
import space.kscience.kmath.expressions.Expression
import space.kscience.kmath.expressions.SymbolIndexer
import space.kscience.kmath.expressions.derivative
import space.kscience.kmath.misc.Symbol import space.kscience.kmath.misc.Symbol
import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.optimization.* import space.kscience.kmath.optimization.*
@ -26,94 +23,98 @@ import kotlin.reflect.KClass
public operator fun PointValuePair.component1(): DoubleArray = point public operator fun PointValuePair.component1(): DoubleArray = point
public operator fun PointValuePair.component2(): Double = value public operator fun PointValuePair.component2(): Double = value
public class CMOptimizerFactory(public val optimizerBuilder: () -> MultivariateOptimizer) : OptimizationFeature
//public class CMOptimizerData(public val )
@OptIn(UnstableKMathAPI::class) @OptIn(UnstableKMathAPI::class)
public class CMOptimization( public class CMOptimization : Optimizer<FunctionOptimization<Double>> {
override val symbols: List<Symbol>,
) : FunctionOptimization<Double>, NoDerivFunctionOptimization<Double>, SymbolIndexer, OptimizationFeature {
private val optimizationData: HashMap<KClass<out OptimizationData>, OptimizationData> = HashMap() override suspend fun process(
private var optimizerBuilder: (() -> MultivariateOptimizer)? = null problem: FunctionOptimization<Double>
public var convergenceChecker: ConvergenceChecker<PointValuePair> = SimpleValueChecker( ): FunctionOptimization<Double> = withSymbols(problem.parameters){
DEFAULT_RELATIVE_TOLERANCE, val cmOptimizer: MultivariateOptimizer =
DEFAULT_ABSOLUTE_TOLERANCE, problem.getFeature<CMOptimizerFactory>()?.optimizerBuilder?.invoke() ?: SimplexOptimizer()
DEFAULT_MAX_ITER
)
override var maximize: Boolean val convergenceChecker: ConvergenceChecker<PointValuePair> = SimpleValueChecker(
get() = optimizationData[GoalType::class] == GoalType.MAXIMIZE DEFAULT_RELATIVE_TOLERANCE,
set(value) { DEFAULT_ABSOLUTE_TOLERANCE,
optimizationData[GoalType::class] = if (value) GoalType.MAXIMIZE else GoalType.MINIMIZE DEFAULT_MAX_ITER
)
val optimizationData: HashMap<KClass<out OptimizationData>, OptimizationData> = HashMap()
fun addOptimizationData(data: OptimizationData) {
optimizationData[data::class] = data
} }
public fun addOptimizationData(data: OptimizationData) {
optimizationData[data::class] = data
}
init {
addOptimizationData(MaxEval.unlimited()) addOptimizationData(MaxEval.unlimited())
} addOptimizationData(InitialGuess(problem.initialGuess.toDoubleArray()))
public fun exportOptimizationData(): List<OptimizationData> = optimizationData.values.toList() fun exportOptimizationData(): List<OptimizationData> = optimizationData.values.toList()
public override fun initialGuess(map: Map<Symbol, Double>): Unit {
addOptimizationData(InitialGuess(map.toDoubleArray()))
}
public override fun function(expression: Expression<Double>): Unit { /**
val objectiveFunction = ObjectiveFunction { * Register no-deriv function instead of differentiable function
val args = it.toMap() */
expression(args) /**
* Register no-deriv function instead of differentiable function
*/
fun noDerivFunction(expression: Expression<Double>): Unit {
val objectiveFunction = ObjectiveFunction {
val args = problem.initialGuess + it.toMap()
expression(args)
}
addOptimizationData(objectiveFunction)
} }
addOptimizationData(objectiveFunction)
}
public override fun diffFunction(expression: DifferentiableExpression<Double, Expression<Double>>) { public override fun function(expression: DifferentiableExpression<Double, Expression<Double>>) {
function(expression) noDerivFunction(expression)
val gradientFunction = ObjectiveFunctionGradient { val gradientFunction = ObjectiveFunctionGradient {
val args = it.toMap() val args = startingPoint + it.toMap()
DoubleArray(symbols.size) { index -> DoubleArray(symbols.size) { index ->
expression.derivative(symbols[index])(args) expression.derivative(symbols[index])(args)
}
}
addOptimizationData(gradientFunction)
if (optimizerBuilder == null) {
optimizerBuilder = {
NonLinearConjugateGradientOptimizer(
NonLinearConjugateGradientOptimizer.Formula.FLETCHER_REEVES,
convergenceChecker
)
}
} }
} }
addOptimizationData(gradientFunction)
if (optimizerBuilder == null) { public fun simplex(simplex: AbstractSimplex) {
optimizerBuilder = { addOptimizationData(simplex)
NonLinearConjugateGradientOptimizer( //Set optimization builder to simplex if it is not present
NonLinearConjugateGradientOptimizer.Formula.FLETCHER_REEVES, if (optimizerBuilder == null) {
convergenceChecker optimizerBuilder = { SimplexOptimizer(convergenceChecker) }
)
} }
} }
}
public fun simplex(simplex: AbstractSimplex) { public fun simplexSteps(steps: Map<Symbol, Double>) {
addOptimizationData(simplex) simplex(NelderMeadSimplex(steps.toDoubleArray()))
//Set optimization builder to simplex if it is not present
if (optimizerBuilder == null) {
optimizerBuilder = { SimplexOptimizer(convergenceChecker) }
} }
}
public fun simplexSteps(steps: Map<Symbol, Double>) { public fun goal(goalType: GoalType) {
simplex(NelderMeadSimplex(steps.toDoubleArray())) addOptimizationData(goalType)
} }
public fun goal(goalType: GoalType) { public fun optimizer(block: () -> MultivariateOptimizer) {
addOptimizationData(goalType) optimizerBuilder = block
} }
public fun optimizer(block: () -> MultivariateOptimizer) { override fun update(result: OptimizationResult<Double>) {
optimizerBuilder = block initialGuess(result.point)
} }
override fun update(result: OptimizationResult<Double>) { override suspend fun optimize(): OptimizationResult<Double> {
initialGuess(result.point) val optimizer = optimizerBuilder?.invoke() ?: error("Optimizer not defined")
} val (point, value) = optimizer.optimize(*optimizationData.values.toTypedArray())
return OptimizationResult(point.toMap(), value)
override fun optimize(): OptimizationResult<Double> { }
val optimizer = optimizerBuilder?.invoke() ?: error("Optimizer not defined") return@withSymbols TODO()
val (point, value) = optimizer.optimize(*optimizationData.values.toTypedArray())
return OptimizationResult(point.toMap(), value, setOf(this))
} }
public companion object : OptimizationProblemFactory<Double, CMOptimization> { public companion object : OptimizationProblemFactory<Double, CMOptimization> {

View File

@ -10,10 +10,7 @@ import space.kscience.kmath.commons.expressions.DerivativeStructureField
import space.kscience.kmath.expressions.DifferentiableExpression import space.kscience.kmath.expressions.DifferentiableExpression
import space.kscience.kmath.expressions.Expression import space.kscience.kmath.expressions.Expression
import space.kscience.kmath.misc.Symbol import space.kscience.kmath.misc.Symbol
import space.kscience.kmath.optimization.FunctionOptimization import space.kscience.kmath.optimization.*
import space.kscience.kmath.optimization.OptimizationResult
import space.kscience.kmath.optimization.noDerivOptimizeWith
import space.kscience.kmath.optimization.optimizeWith
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.asBuffer import space.kscience.kmath.structures.asBuffer
@ -46,20 +43,25 @@ public fun FunctionOptimization.Companion.chiSquared(
/** /**
* Optimize expression without derivatives * Optimize expression without derivatives
*/ */
public fun Expression<Double>.optimize( public suspend fun Expression<Double>.optimize(
vararg symbols: Symbol, vararg symbols: Symbol,
configuration: CMOptimization.() -> Unit, configuration: CMOptimization.() -> Unit,
): OptimizationResult<Double> = noDerivOptimizeWith(CMOptimization, symbols = symbols, configuration) ): OptimizationResult<Double> {
require(symbols.isNotEmpty()) { "Must provide a list of symbols for optimization" }
val problem = CMOptimization(symbols.toList(), configuration)
problem.noDerivFunction(this)
return problem.optimize()
}
/** /**
* Optimize differentiable expression * Optimize differentiable expression
*/ */
public fun DifferentiableExpression<Double, Expression<Double>>.optimize( public suspend fun DifferentiableExpression<Double, Expression<Double>>.optimize(
vararg symbols: Symbol, vararg symbols: Symbol,
configuration: CMOptimization.() -> Unit, configuration: CMOptimization.() -> Unit,
): OptimizationResult<Double> = optimizeWith(CMOptimization, symbols = symbols, configuration) ): OptimizationResult<Double> = optimizeWith(CMOptimization, symbols = symbols, configuration)
public fun DifferentiableExpression<Double, Expression<Double>>.minimize( public suspend fun DifferentiableExpression<Double, Expression<Double>>.minimize(
vararg startPoint: Pair<Symbol, Double>, vararg startPoint: Pair<Symbol, Double>,
configuration: CMOptimization.() -> Unit = {}, configuration: CMOptimization.() -> Unit = {},
): OptimizationResult<Double> { ): OptimizationResult<Double> {
@ -67,7 +69,7 @@ public fun DifferentiableExpression<Double, Expression<Double>>.minimize(
return optimize(*symbols){ return optimize(*symbols){
maximize = false maximize = false
initialGuess(startPoint.toMap()) initialGuess(startPoint.toMap())
diffFunction(this@minimize) function(this@minimize)
configuration() configuration()
} }
} }

View File

@ -24,7 +24,7 @@ internal class OptimizeTest {
} }
@Test @Test
fun testGradientOptimization() { fun testGradientOptimization() = runBlocking{
val result = normal.optimize(x, y) { val result = normal.optimize(x, y) {
initialGuess(x to 1.0, y to 1.0) initialGuess(x to 1.0, y to 1.0)
//no need to select optimizer. Gradient optimizer is used by default because gradients are provided by function //no need to select optimizer. Gradient optimizer is used by default because gradients are provided by function
@ -34,7 +34,7 @@ internal class OptimizeTest {
} }
@Test @Test
fun testSimplexOptimization() { fun testSimplexOptimization() = runBlocking{
val result = normal.optimize(x, y) { val result = normal.optimize(x, y) {
initialGuess(x to 1.0, y to 1.0) initialGuess(x to 1.0, y to 1.0)
simplexSteps(x to 2.0, y to 0.5) simplexSteps(x to 2.0, y to 0.5)

View File

@ -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
}
}

View File

@ -10,7 +10,7 @@ import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
/** /**
* A [XYColumnarData] with guaranteed [x], [y] and [z] columns designated by corresponding symbols. * A [ColumnarData] with guaranteed [x], [y] and [z] columns designated by corresponding symbols.
* Inherits [XYColumnarData]. * Inherits [XYColumnarData].
*/ */
@UnstableKMathAPI @UnstableKMathAPI

View File

@ -51,6 +51,6 @@ public abstract class FirstDerivativeExpression<T, R : Expression<T>> : Differen
/** /**
* A factory that converts an expression in autodiff variables to a [DifferentiableExpression] * A factory that converts an expression in autodiff variables to a [DifferentiableExpression]
*/ */
public fun interface AutoDiffProcessor<T : Any, I : Any, A : ExpressionAlgebra<T, I>, out R : Expression<T>> { public fun interface AutoDiffProcessor<T, I, A : ExpressionAlgebra<T, I>, out R : Expression<T>> {
public fun process(function: A.() -> I): DifferentiableExpression<T, R> public fun process(function: A.() -> I): DifferentiableExpression<T, R>
} }

View File

@ -10,6 +10,7 @@ import space.kscience.kmath.misc.Symbol
import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.nd.Structure2D import space.kscience.kmath.nd.Structure2D
import space.kscience.kmath.structures.BufferFactory import space.kscience.kmath.structures.BufferFactory
import space.kscience.kmath.structures.DoubleBuffer
import kotlin.jvm.JvmInline import kotlin.jvm.JvmInline
/** /**
@ -46,6 +47,11 @@ public interface SymbolIndexer {
return symbols.indices.associate { symbols[it] to get(it) } return symbols.indices.associate { symbols[it] to get(it) }
} }
public fun <T> Point<T>.toMap(): Map<Symbol, T> {
require(size == symbols.size) { "The input array size for indexer should be ${symbols.size} but $size found" }
return symbols.indices.associate { symbols[it] to get(it) }
}
public operator fun <T> Structure2D<T>.get(rowSymbol: Symbol, columnSymbol: Symbol): T = public operator fun <T> Structure2D<T>.get(rowSymbol: Symbol, columnSymbol: Symbol): T =
get(indexOf(rowSymbol), indexOf(columnSymbol)) get(indexOf(rowSymbol), indexOf(columnSymbol))
@ -55,6 +61,10 @@ public interface SymbolIndexer {
public fun <T> Map<Symbol, T>.toPoint(bufferFactory: BufferFactory<T>): Point<T> = public fun <T> Map<Symbol, T>.toPoint(bufferFactory: BufferFactory<T>): Point<T> =
bufferFactory(symbols.size) { getValue(symbols[it]) } bufferFactory(symbols.size) { getValue(symbols[it]) }
public fun Map<Symbol, Double>.toPoint(): DoubleBuffer =
DoubleBuffer(symbols.size) { getValue(symbols[it]) }
public fun Map<Symbol, Double>.toDoubleArray(): DoubleArray = DoubleArray(symbols.size) { getValue(symbols[it]) } public fun Map<Symbol, Double>.toDoubleArray(): DoubleArray = DoubleArray(symbols.size) { getValue(symbols[it]) }
} }

View File

@ -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
}

View File

@ -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 })
}
}

View File

@ -13,7 +13,7 @@ import space.kscience.kmath.nd.as2D
/** /**
* A context that allows to operate on a [MutableBuffer] as on 2d array * A context that allows to operate on a [MutableBuffer] as on 2d array
*/ */
internal class BufferAccessor2D<T : Any>( internal class BufferAccessor2D<T>(
public val rowNum: Int, public val rowNum: Int,
public val colNum: Int, public val colNum: Int,
val factory: MutableBufferFactory<T>, val factory: MutableBufferFactory<T>,

View File

@ -5,8 +5,10 @@
package space.kscience.kmath.structures package space.kscience.kmath.structures
import space.kscience.kmath.linear.Point
import space.kscience.kmath.operations.ExtendedField import space.kscience.kmath.operations.ExtendedField
import space.kscience.kmath.operations.ExtendedFieldOperations import space.kscience.kmath.operations.ExtendedFieldOperations
import space.kscience.kmath.operations.Norm
import kotlin.math.* import kotlin.math.*
/** /**
@ -161,12 +163,16 @@ public object DoubleBufferFieldOperations : ExtendedFieldOperations<Buffer<Doubl
DoubleBuffer(DoubleArray(arg.size) { ln(arg[it]) }) DoubleBuffer(DoubleArray(arg.size) { ln(arg[it]) })
} }
public object DoubleL2Norm : Norm<Point<Double>, Double> {
override fun norm(arg: Point<Double>): Double = sqrt(arg.fold(0.0) { acc: Double, d: Double -> acc + d.pow(2) })
}
/** /**
* [ExtendedField] over [DoubleBuffer]. * [ExtendedField] over [DoubleBuffer].
* *
* @property size the size of buffers to operate on. * @property size the size of buffers to operate on.
*/ */
public class DoubleBufferField(public val size: Int) : ExtendedField<Buffer<Double>> { public class DoubleBufferField(public val size: Int) : ExtendedField<Buffer<Double>>, Norm<Buffer<Double>, Double> {
public override val zero: Buffer<Double> by lazy { DoubleBuffer(size) { 0.0 } } public override val zero: Buffer<Double> by lazy { DoubleBuffer(size) { 0.0 } }
public override val one: Buffer<Double> by lazy { DoubleBuffer(size) { 1.0 } } public override val one: Buffer<Double> by lazy { DoubleBuffer(size) { 1.0 } }
@ -274,4 +280,6 @@ public class DoubleBufferField(public val size: Int) : ExtendedField<Buffer<Doub
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" } require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
return DoubleBufferFieldOperations.ln(arg) return DoubleBufferFieldOperations.ln(arg)
} }
override fun norm(arg: Buffer<Double>): Double = DoubleL2Norm.norm(arg)
} }

View File

@ -8,11 +8,8 @@ package space.kscience.kmath.real
import space.kscience.kmath.linear.Point import space.kscience.kmath.linear.Point
import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.operations.Norm import space.kscience.kmath.operations.Norm
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.*
import space.kscience.kmath.structures.MutableBuffer.Companion.double import space.kscience.kmath.structures.MutableBuffer.Companion.double
import space.kscience.kmath.structures.asBuffer
import space.kscience.kmath.structures.fold
import space.kscience.kmath.structures.indices
import kotlin.math.pow import kotlin.math.pow
import kotlin.math.sqrt import kotlin.math.sqrt
@ -105,8 +102,4 @@ public fun DoubleVector.sum(): Double {
return res return res
} }
public object VectorL2Norm : Norm<DoubleVector, Double> { public val DoubleVector.norm: Double get() = DoubleL2Norm.norm(this)
override fun norm(arg: DoubleVector): Double = sqrt(arg.fold(0.0) { acc: Double, d: Double -> acc + d.pow(2) })
}
public val DoubleVector.norm: Double get() = VectorL2Norm.norm(this)

View File

@ -50,7 +50,7 @@ public class GaussIntegrator<T : Any>(
} }
} }
override fun integrate(integrand: UnivariateIntegrand<T>): UnivariateIntegrand<T> = with(algebra) { override fun process(integrand: UnivariateIntegrand<T>): UnivariateIntegrand<T> = with(algebra) {
val f = integrand.function val f = integrand.function
val (points, weights) = buildRule(integrand) val (points, weights) = buildRule(integrand)
var res = zero var res = zero
@ -63,7 +63,7 @@ public class GaussIntegrator<T : Any>(
c = t - res - y c = t - res - y
res = t res = t
} }
return integrand + IntegrandValue(res) + IntegrandCallsPerformed(integrand.calls + points.size) return integrand.with(IntegrandValue(res),IntegrandCallsPerformed(integrand.calls + points.size))
} }
public companion object { public companion object {
@ -80,17 +80,17 @@ public class GaussIntegrator<T : Any>(
* * [UnivariateIntegrandRanges] - Set of ranges and number of points per range. Defaults to given [IntegrationRange] and [IntegrandMaxCalls] * * [UnivariateIntegrandRanges] - Set of ranges and number of points per range. Defaults to given [IntegrationRange] and [IntegrandMaxCalls]
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public fun <T : Any> Field<T>.integrate( public fun <T : Any> Field<T>.process(
vararg features: IntegrandFeature, vararg features: IntegrandFeature,
function: (Double) -> T, function: (Double) -> T,
): UnivariateIntegrand<T> = GaussIntegrator(this).integrate(UnivariateIntegrand(function, *features)) ): UnivariateIntegrand<T> = GaussIntegrator(this).process(UnivariateIntegrand(function, *features))
/** /**
* Use [GaussIntegrator.Companion.integrate] to integrate the function in the current algebra with given [range] and [numPoints] * Use [GaussIntegrator.Companion.integrate] to integrate the function in the current algebra with given [range] and [numPoints]
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public fun <T : Any> Field<T>.integrate( public fun <T : Any> Field<T>.process(
range: ClosedRange<Double>, range: ClosedRange<Double>,
order: Int = 10, order: Int = 10,
intervals: Int = 10, intervals: Int = 10,
@ -104,7 +104,7 @@ public fun <T : Any> Field<T>.integrate(
val ranges = UnivariateIntegrandRanges( val ranges = UnivariateIntegrandRanges(
(0 until intervals).map { i -> (rangeSize * i)..(rangeSize * (i + 1)) to order } (0 until intervals).map { i -> (rangeSize * i)..(rangeSize * (i + 1)) to order }
) )
return GaussIntegrator(this).integrate( return GaussIntegrator(this).process(
UnivariateIntegrand( UnivariateIntegrand(
function, function,
IntegrationRange(range), IntegrationRange(range),

View File

@ -5,12 +5,15 @@
package space.kscience.kmath.integration package space.kscience.kmath.integration
import space.kscience.kmath.misc.FeatureSet
import space.kscience.kmath.misc.Featured
import kotlin.reflect.KClass import kotlin.reflect.KClass
public interface IntegrandFeature public interface IntegrandFeature
public interface Integrand { public interface Integrand: Featured<IntegrandFeature>{
public fun <T : IntegrandFeature> getFeature(type: KClass<T>): T? public val features: FeatureSet<IntegrandFeature>
override fun <T : IntegrandFeature> getFeature(type: KClass<out T>): T? = features.getFeature(type)
} }
public inline fun <reified T : IntegrandFeature> Integrand.getFeature(): T? = getFeature(T::class) public inline fun <reified T : IntegrandFeature> Integrand.getFeature(): T? = getFeature(T::class)

View File

@ -12,5 +12,5 @@ public interface Integrator<I : Integrand> {
/** /**
* Runs one integration pass and return a new [Integrand] with a new set of features. * Runs one integration pass and return a new [Integrand] with a new set of features.
*/ */
public fun integrate(integrand: I): I public fun process(integrand: I): I
} }

View File

@ -6,27 +6,21 @@
package space.kscience.kmath.integration package space.kscience.kmath.integration
import space.kscience.kmath.linear.Point import space.kscience.kmath.linear.Point
import space.kscience.kmath.misc.FeatureSet
import kotlin.reflect.KClass import kotlin.reflect.KClass
public class MultivariateIntegrand<T : Any> internal constructor( public class MultivariateIntegrand<T : Any> internal constructor(
private val features: Map<KClass<*>, IntegrandFeature>, override val features: FeatureSet<IntegrandFeature>,
public val function: (Point<T>) -> T, public val function: (Point<T>) -> T,
) : Integrand { ) : Integrand
@Suppress("UNCHECKED_CAST") public fun <T : Any> MultivariateIntegrand<T>.with(vararg newFeatures: IntegrandFeature): MultivariateIntegrand<T> =
override fun <T : IntegrandFeature> getFeature(type: KClass<T>): T? = features[type] as? T MultivariateIntegrand(features.with(*newFeatures), function)
public operator fun <F : IntegrandFeature> plus(pair: Pair<KClass<out F>, F>): MultivariateIntegrand<T> =
MultivariateIntegrand(features + pair, function)
public operator fun <F : IntegrandFeature> plus(feature: F): MultivariateIntegrand<T> =
plus(feature::class to feature)
}
@Suppress("FunctionName") @Suppress("FunctionName")
public fun <T : Any> MultivariateIntegrand( public fun <T : Any> MultivariateIntegrand(
vararg features: IntegrandFeature, vararg features: IntegrandFeature,
function: (Point<T>) -> T, function: (Point<T>) -> T,
): MultivariateIntegrand<T> = MultivariateIntegrand(features.associateBy { it::class }, function) ): MultivariateIntegrand<T> = MultivariateIntegrand(FeatureSet.of(*features), function)
public val <T : Any> MultivariateIntegrand<T>.value: T? get() = getFeature<IntegrandValue<T>>()?.value public val <T : Any> MultivariateIntegrand<T>.value: T? get() = getFeature<IntegrandValue<T>>()?.value

View File

@ -5,30 +5,24 @@
package space.kscience.kmath.integration package space.kscience.kmath.integration
import space.kscience.kmath.misc.FeatureSet
import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.misc.UnstableKMathAPI
import kotlin.jvm.JvmInline import kotlin.jvm.JvmInline
import kotlin.reflect.KClass
public class UnivariateIntegrand<T : Any> internal constructor( public class UnivariateIntegrand<T : Any> internal constructor(
private val features: Map<KClass<*>, IntegrandFeature>, override val features: FeatureSet<IntegrandFeature>,
public val function: (Double) -> T, public val function: (Double) -> T,
) : Integrand { ) : Integrand
@Suppress("UNCHECKED_CAST") public fun <T : Any> UnivariateIntegrand<T>.with(vararg newFeatures: IntegrandFeature): UnivariateIntegrand<T> =
override fun <T : IntegrandFeature> getFeature(type: KClass<T>): T? = features[type] as? T UnivariateIntegrand(features.with(*newFeatures), function)
public operator fun <F : IntegrandFeature> plus(pair: Pair<KClass<out F>, F>): UnivariateIntegrand<T> =
UnivariateIntegrand(features + pair, function)
public operator fun <F : IntegrandFeature> plus(feature: F): UnivariateIntegrand<T> =
plus(feature::class to feature)
}
@Suppress("FunctionName") @Suppress("FunctionName")
public fun <T : Any> UnivariateIntegrand( public fun <T : Any> UnivariateIntegrand(
function: (Double) -> T, function: (Double) -> T,
vararg features: IntegrandFeature, vararg features: IntegrandFeature,
): UnivariateIntegrand<T> = UnivariateIntegrand(features.associateBy { it::class }, function) ): UnivariateIntegrand<T> = UnivariateIntegrand(FeatureSet.of(*features), function)
public typealias UnivariateIntegrator<T> = Integrator<UnivariateIntegrand<T>> public typealias UnivariateIntegrator<T> = Integrator<UnivariateIntegrand<T>>
@ -46,7 +40,7 @@ public fun UnivariateIntegrator<Double>.integrate(
range: ClosedRange<Double>, range: ClosedRange<Double>,
vararg features: IntegrandFeature, vararg features: IntegrandFeature,
function: (Double) -> Double, function: (Double) -> Double,
): Double = integrate( ): Double = process(
UnivariateIntegrand(function, IntegrationRange(range), *features) UnivariateIntegrand(function, IntegrationRange(range), *features)
).value ?: error("Unexpected: no value after integration.") ).value ?: error("Unexpected: no value after integration.")
@ -65,7 +59,7 @@ public fun UnivariateIntegrator<Double>.integrate(
featureBuilder() featureBuilder()
add(IntegrationRange(range)) add(IntegrationRange(range))
} }
return integrate( return process(
UnivariateIntegrand(function, *features.toTypedArray()) UnivariateIntegrand(function, *features.toTypedArray())
).value ?: error("Unexpected: no value after integration.") ).value ?: error("Unexpected: no value after integration.")
} }

View File

@ -16,7 +16,7 @@ import kotlin.test.assertEquals
class GaussIntegralTest { class GaussIntegralTest {
@Test @Test
fun gaussSin() { fun gaussSin() {
val res = DoubleField.integrate(0.0..2 * PI) { x -> val res = DoubleField.process(0.0..2 * PI) { x ->
sin(x) sin(x)
} }
assertEquals(0.0, res.value!!, 1e-2) assertEquals(0.0, res.value!!, 1e-2)
@ -24,7 +24,7 @@ class GaussIntegralTest {
@Test @Test
fun gaussUniform() { fun gaussUniform() {
val res = DoubleField.integrate(0.0..100.0) { x -> val res = DoubleField.process(0.0..100.0) { x ->
if(x in 30.0..50.0){ if(x in 30.0..50.0){
1.0 1.0
} else { } else {

View File

@ -9,86 +9,97 @@ import space.kscience.kmath.expressions.AutoDiffProcessor
import space.kscience.kmath.expressions.DifferentiableExpression import space.kscience.kmath.expressions.DifferentiableExpression
import space.kscience.kmath.expressions.Expression import space.kscience.kmath.expressions.Expression
import space.kscience.kmath.expressions.ExpressionAlgebra import space.kscience.kmath.expressions.ExpressionAlgebra
import space.kscience.kmath.misc.FeatureSet
import space.kscience.kmath.misc.Symbol import space.kscience.kmath.misc.Symbol
import space.kscience.kmath.operations.ExtendedField import space.kscience.kmath.operations.ExtendedField
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.indices import space.kscience.kmath.structures.indices
/**
* A likelihood function optimization problem with provided derivatives
*/
public interface FunctionOptimization<T : Any> : Optimization<T> {
/**
* The optimization direction. If true search for function maximum, if false, search for the minimum
*/
public var maximize: Boolean
/** public class FunctionOptimization<T : Any>(
* Define the initial guess for the optimization problem override val features: FeatureSet<OptimizationFeature>,
*/ public val expression: DifferentiableExpression<T, Expression<T>>,
public fun initialGuess(map: Map<Symbol, T>) public val initialGuess: Map<Symbol, T>,
public val parameters: Collection<Symbol>,
public val maximize: Boolean,
) : OptimizationProblem
/** //
* Set a differentiable expression as objective function as function and gradient provider ///**
*/ // * A likelihood function optimization problem with provided derivatives
public fun diffFunction(expression: DifferentiableExpression<T, Expression<T>>) // */
//public interface FunctionOptimizationBuilder<T : Any> {
public companion object { // /**
/** // * The optimization direction. If true search for function maximum, if false, search for the minimum
* Generate a chi squared expression from given x-y-sigma data and inline model. Provides automatic differentiation // */
*/ // public var maximize: Boolean
public fun <T : Any, I : Any, A> chiSquared( //
autoDiff: AutoDiffProcessor<T, I, A, Expression<T>>, // /**
x: Buffer<T>, // * Define the initial guess for the optimization problem
y: Buffer<T>, // */
yErr: Buffer<T>, // public fun initialGuess(map: Map<Symbol, T>)
model: A.(I) -> I, //
): DifferentiableExpression<T, Expression<T>> where A : ExtendedField<I>, A : ExpressionAlgebra<T, I> { // /**
require(x.size == y.size) { "X and y buffers should be of the same size" } // * Set a differentiable expression as objective function as function and gradient provider
require(y.size == yErr.size) { "Y and yErr buffer should of the same size" } // */
// public fun function(expression: DifferentiableExpression<T, Expression<T>>)
return autoDiff.process { //
var sum = zero // public companion object {
// /**
x.indices.forEach { // * Generate a chi squared expression from given x-y-sigma data and inline model. Provides automatic differentiation
val xValue = const(x[it]) // */
val yValue = const(y[it]) // public fun <T : Any, I : Any, A> chiSquared(
val yErrValue = const(yErr[it]) // autoDiff: AutoDiffProcessor<T, I, A, Expression<T>>,
val modelValue = model(xValue) // x: Buffer<T>,
sum += ((yValue - modelValue) / yErrValue).pow(2) // y: Buffer<T>,
} // yErr: Buffer<T>,
// model: A.(I) -> I,
sum // ): DifferentiableExpression<T, Expression<T>> where A : ExtendedField<I>, A : ExpressionAlgebra<T, I> {
} // require(x.size == y.size) { "X and y buffers should be of the same size" }
} // require(y.size == yErr.size) { "Y and yErr buffer should of the same size" }
} //
} // return autoDiff.process {
// var sum = zero
/** //
* Define a chi-squared-based objective function // x.indices.forEach {
*/ // val xValue = const(x[it])
public fun <T: Any, I : Any, A> FunctionOptimization<T>.chiSquared( // val yValue = const(y[it])
autoDiff: AutoDiffProcessor<T, I, A, Expression<T>>, // val yErrValue = const(yErr[it])
x: Buffer<T>, // val modelValue = model(xValue)
y: Buffer<T>, // sum += ((yValue - modelValue) / yErrValue).pow(2)
yErr: Buffer<T>, // }
model: A.(I) -> I, //
) where A : ExtendedField<I>, A : ExpressionAlgebra<T, I> { // sum
val chiSquared = FunctionOptimization.chiSquared(autoDiff, x, y, yErr, model) // }
diffFunction(chiSquared) // }
maximize = false // }
} //}
//
/** ///**
* Optimize differentiable expression using specific [OptimizationProblemFactory] // * Define a chi-squared-based objective function
*/ // */
public fun <T : Any, F : FunctionOptimization<T>> DifferentiableExpression<T, Expression<T>>.optimizeWith( //public fun <T : Any, I : Any, A> FunctionOptimization<T>.chiSquared(
factory: OptimizationProblemFactory<T, F>, // autoDiff: AutoDiffProcessor<T, I, A, Expression<T>>,
vararg symbols: Symbol, // x: Buffer<T>,
configuration: F.() -> Unit, // y: Buffer<T>,
): OptimizationResult<T> { // yErr: Buffer<T>,
require(symbols.isNotEmpty()) { "Must provide a list of symbols for optimization" } // model: A.(I) -> I,
val problem = factory(symbols.toList(), configuration) //) where A : ExtendedField<I>, A : ExpressionAlgebra<T, I> {
problem.diffFunction(this) // val chiSquared = FunctionOptimization.chiSquared(autoDiff, x, y, yErr, model)
return problem.optimize() // function(chiSquared)
} // maximize = false
//}
//
///**
// * Optimize differentiable expression using specific [OptimizationProblemFactory]
// */
//public suspend fun <T : Any, F : FunctionOptimization<T>> DifferentiableExpression<T, Expression<T>>.optimizeWith(
// factory: OptimizationProblemFactory<T, F>,
// vararg symbols: Symbol,
// configuration: F.() -> Unit,
//): OptimizationResult<T> {
// require(symbols.isNotEmpty()) { "Must provide a list of symbols for optimization" }
// val problem = factory(symbols.toList(), configuration)
// problem.function(this)
// return problem.optimize()
//}

View File

@ -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()
}

View File

@ -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)

View File

@ -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
}

View File

@ -16,7 +16,7 @@ import space.kscience.kmath.operations.ExtendedField
import space.kscience.kmath.operations.Field import space.kscience.kmath.operations.Field
@UnstableKMathAPI @UnstableKMathAPI
public interface XYFit<T : Any> : Optimization<T> { public interface XYFit<T> : OptimizationProblem<T> {
public val algebra: Field<T> public val algebra: Field<T>

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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)
}
}

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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()
}
}
}
}

View File

@ -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"
}
}

View File

@ -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();
}
}
}

View File

@ -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))
}
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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?
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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()
}
}

View File

@ -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()
}
}

View File

@ -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
}
}

View File

@ -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)
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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()
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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)
}
}

View File

@ -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)
}

View File

@ -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)
}
}

View File

@ -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
}
}
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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)
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}
}

View File

@ -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())
}
}

View File

@ -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()
}
}

View File

@ -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
}
}

View File

@ -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())
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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)
}
}

View File

@ -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))
}
}

View File

@ -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()
}
}

View File

@ -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()
}
}

View File

@ -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()
}
}

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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())
}
}

View File

@ -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
}
}

View File

@ -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)

View File

@ -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())
}
}

View File

@ -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()
}
}

View File

@ -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())
}
}

View File

@ -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()
}
}

View File

@ -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
}
}

View File

@ -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())
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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()
}
}

View File

@ -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
}
}

View File

@ -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()
}
}

View File

@ -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

View File

@ -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"
}
}