This commit is contained in:
Alexander Nozik 2021-04-11 22:16:25 +03:00
parent a85fddb5f1
commit c6beaf3f83
81 changed files with 11396 additions and 11 deletions

View File

@ -13,7 +13,7 @@ allprojects {
}
val dataforgeVersion by extra("0.4.0-dev-2")
val kmathVersion by extra("0.2.1")
val kmathVersion by extra("0.3.0-dev-3")
apiValidation{
validationDisabled = true

View File

@ -10,7 +10,7 @@ message Point {
// Raw data frame
message Frame {
uint64 time = 1; // Time in nanos from the beginning of the block
bytes data = 2; // Frame data as an array of int16 mesured in arbitrary channels
bytes data = 2; // Frame data as an array of int16 measured in arbitrary channels
}
// Event block obtained directly from device of from frame analysis
// In order to save space, times and amplitudes are in separate arrays.

View File

@ -0,0 +1,24 @@
plugins {
kotlin("multiplatform")
id("ru.mipt.npm.gradle.common")
`maven-publish`
}
val dataforgeVersion: String by rootProject.extra
val kmathVersion: String by rootProject.extra
kotlin.sourceSets {
commonMain {
dependencies {
api("space.kscience:dataforge-meta:$dataforgeVersion")
api("space.kscience:kmath-for-real:$kmathVersion")
}
}
jvmMain{
dependencies{
api("space.kscience:kmath-commons:$kmathVersion")
api("ch.qos.logback:logback-classic:1.2.3")
}
}
}

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 hep.dataforge.stat.fit
/**
* Контейнер для несимметричных оценок и доверительных интервалов
*
* @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,216 @@
/*
* 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 hep.dataforge.stat.fit
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,97 @@
/*
* 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 hep.dataforge.stat.fit
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,132 @@
/*
* 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 hep.dataforge.stat.fit
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,261 @@
/*
* Copyright 2018 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.fit
import hep.dataforge.io.history.Chronicle
import hep.dataforge.io.history.History
import hep.dataforge.maths.MathUtils
import hep.dataforge.maths.MatrixOperations.inverse
import hep.dataforge.maths.NamedMatrix
import hep.dataforge.maths.NamedVector
import hep.dataforge.meta.Meta
import hep.dataforge.names.AbstractNamedSet
import hep.dataforge.stat.fit.FitStage.*
import hep.dataforge.stat.fit.Fitter.Companion.getFitPars
import hep.dataforge.utils.Misc
import org.apache.commons.math3.linear.ArrayRealVector
import org.apache.commons.math3.linear.EigenDecomposition
/**
* The state of QOW fitter
* Created by darksnake on 17-Oct-16.
*/
internal class QOWeight(val source: FitState, val fitPars: Array<String>, theta: ParamSet) : AbstractNamedSet(fitPars) {
/**
* The set of parameters for which the weight is calculated
* TODO make paramSet immutable
*/
val theta = theta.copy()
/**
* Derivatives of the spectrum over parameters. First index in the point number, second one - index of parameter
*
* @return the derivs
*/
val derivs: Array<DoubleArray>
/**
* Array of dispersions in each point
*
* @return the dispersion
*/
val dispersion: DoubleArray
init {
if (source.dataSize <= 0) {
throw IllegalStateException("The state does not contain data")
}
dispersion = DoubleArray(source.dataSize)
derivs = Array(names.size()) { DoubleArray(source.dataSize) }
(0 until source.dataSize).forEach { i ->
this.dispersion[i] = source.getDispersion(i, theta)
(0 until names.size()).forEach { k ->
derivs[k][i] = source.getDisDeriv(this.names.get(k), i, theta)
}
}
}
}
/**
*
*
* QOWFitter class.
*
* @author Alexander Nozik
* @version $Id: $Id
*/
object QOWFitter : Fitter {
/**
* Constant `QOW_ENGINE_NAME="QOW"`
*/
const val QOW_ENGINE_NAME = "QOW"
/**
* Constant `QOW_METHOD_FAST="fast"`
*/
const val QOW_METHOD_FAST = "fast"
private fun newtonianRun(state: FitState, weight: QOWeight, log: History, meta: Meta) : ParamSet {
val maxSteps = meta.getInt("iterations", 100)
val tolerance = meta.getDouble("tolerance", 0.0)
var dis: Double//норма невязки
// Для удобства работаем всегда с полным набором параметров
var par = state.parameters.copy()
log.report("Starting newtonian iteration from: \n\t{}",
MathUtils.toString(par, *weight.namesAsArray()))
var eqvalues = QOWUtils.getEqValues(state, par, weight)//значения функций
dis = eqvalues.vector.norm// невязка
log.report("Starting discrepancy is {}", dis)
var i = 0
var flag = false
while (!flag) {
Misc.checkThread()
i++
log.report("Starting step number {}", i)
val currentSolution = if (meta.getString(METHOD_NAME,"").equals(QOW_METHOD_FAST, ignoreCase = true)) {
//Берет значения матрицы в той точке, где считается вес
fastNewtonianStep(state, par, eqvalues, weight)
} else {
//Берет значения матрицы в точке par
newtonianStep(state, par, eqvalues, weight)
}
// здесь должен стоять учет границ параметров
log.report("Parameter values after step are: \n\t{}",
MathUtils.toString(currentSolution, *weight.namesAsArray()))
eqvalues = QOWUtils.getEqValues(state, currentSolution, weight)
val currentDis = eqvalues.vector.norm// невязка после шага
log.report("The discrepancy after step is: {}", currentDis)
if (currentDis >= dis && i > 1) {
//дополнительно проверяем, чтобы был сделан хотя бы один шаг
flag = true
log.report("The discrepancy does not decrease. Stopping iteration.")
} else {
par = currentSolution
dis = currentDis
}
if (i >= maxSteps) {
flag = true
log.report("Maximum number of iterations reached. Stopping iteration.")
}
if (dis <= tolerance) {
flag = true
log.report("Tolerance threshold is reached. Stopping iteration.")
}
}
return par
}
private fun newtonianStep(source: FitState, par: ParamSet, eqvalues: NamedVector, weight: QOWeight): ParamSet {
Misc.checkThread()// check if action is cacneled
val start = par.getParValues(*weight.namesAsArray()).vector
val invJacob = inverse(QOWUtils.getEqDerivValues(source, par, weight))
val step = invJacob.operate(ArrayRealVector(eqvalues.getArray()))
return par.copy().setParValues(NamedVector(weight.namesAsArray(), start.subtract(step)))
}
private fun fastNewtonianStep(source: FitState, par: ParamSet, eqvalues: NamedVector, weight: QOWeight): ParamSet {
Misc.checkThread()// check if action is cacneled
val start = par.getParValues(*weight.namesAsArray()).vector
val invJacob = inverse(QOWUtils.getEqDerivValues(source, weight))
val step = invJacob.operate(ArrayRealVector(eqvalues.getArray()))
return par.copy().setParValues(NamedVector(weight.namesAsArray(), start.subtract(step)))
}
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")
}
}
override val name: String = QOW_ENGINE_NAME
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 = this.newtonianRun(state, curWeight, log, meta)
/*Генерация результата*/
return FitResult.build(state.edit().setPars(res).build(), *fitPars)
}
/**
*
*
* generateErrors.
*
* @param state a [hep.dataforge.stat.fit.FitState] object.
* @param task a [hep.dataforge.stat.fit.FitStage] object.
* @param log a [History] object.
* @return a [FitResult] object.
*/
private fun generateErrors(state: FitState, log: History, meta: Meta): FitResult {
log.report("Starting errors estimation 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))
// ParamSet pars = state.getParameters().copy();
val covar = getCovariance(state, 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
}
}
return FitResult.build(
state.edit().setCovariance(covar, true).build(),
valid,
*fitPars
)
}
private fun getCovariance(source: FitState, weight: QOWeight): NamedMatrix {
val invH = inverse(QOWUtils.getEqDerivValues(source, weight.namesAsArray(), weight))
return NamedMatrix(weight.namesAsArray(), invH)
}
}

View File

@ -0,0 +1,306 @@
/*
* 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.fit
import hep.dataforge.maths.NamedVector
import org.apache.commons.math3.linear.Array2DRowRealMatrix
import org.apache.commons.math3.linear.RealMatrix
import java.util.logging.Logger
/**
*
* @author Alexander Nozik
*/
internal object QOWUtils {
fun covarFExp(source: FitState, set: ParamSet, weight: QOWeight): RealMatrix {
return covarFExp(source, set, weight.namesAsArray(), weight)
}
/**
* Теоретическая ковариация весовых функций.
*
* D(\phi)=E(\phi_k(\theta_0) \phi_l(\theta_0))= disDeriv_k * disDeriv_l /sigma^2
*
*
* @param source
* @param weight
* @return
*/
fun covarF(source: FitState, weight: QOWeight): RealMatrix {
val fitDim = weight.names.size()
val res = Array(fitDim) { DoubleArray(fitDim) }
var i: Int
var k: Int
var l: Int
var summ: Double
k = 0
while (k < fitDim) {
l = k
while (l < fitDim) {
summ = 0.0
i = 0
while (i < source.dataSize) {
summ += weight.derivs[k][i] * weight.derivs[l][i] / weight.dispersion[i]
i++
}
res[k][l] = summ
if (l != k) {
res[l][k] = summ
}
l++
}
k++
}
return Array2DRowRealMatrix(res)
}
/**
* Экспериментальная ковариация весов. Формула (22) из
* http://arxiv.org/abs/physics/0604127
*
* @param source
* @param set
* @param fitPars
* @param weight
* @return
*/
fun covarFExp(source: FitState, set: ParamSet, fitPars: Array<String>, weight: QOWeight): RealMatrix {
val fitDim = fitPars.size
val res = Array(fitDim) { DoubleArray(fitDim) }
val eqvalues = Array(source.dataSize) { DoubleArray(fitDim) }
/*
* Важно! Если не делать предварителього вычисления этих производных, то
* количество вызывов функции будет dim^2 вместо dim Первый индекс -
* номер точки, второй - номер переменной, по которой берется производная
*/
var i: Int
var k: Int
var l: Int
l = 0
while (l < fitDim) {
i = 0
while (i < source.dataSize) {
eqvalues[i][l] = source.getDis(i, set) * weight.derivs[l][i] / weight.dispersion[i]
i++
}
l++
}
var summ: Double
k = 0
while (k < fitDim) {
l = 0
while (l < fitDim) {
summ = 0.0
i = 0
while (i < source.dataSize) {
summ += eqvalues[i][l] * eqvalues[i][k]
i++
}
res[k][l] = summ
l++
}
k++
}
return Array2DRowRealMatrix(res)
}
/**
* Берет производные уравнений по параметрам, указанным в весе
*
* @param source
* @param set
* @param weight
* @return
*/
fun getEqDerivValues(source: FitState, set: ParamSet, weight: QOWeight): RealMatrix {
return getEqDerivValues(source, set, weight.namesAsArray(), weight)
}
fun getEqDerivValues(source: FitState, weight: QOWeight): RealMatrix {
return getEqDerivValues(source, weight.namesAsArray(), weight)
}
/**
* производные уравнений для метода Ньютона
*
* @param source
* @param set
* @param fitPars
* @param weight
* @return
*/
fun getEqDerivValues(source: FitState, set: ParamSet, fitPars: Array<String>, weight: QOWeight): RealMatrix {
val fitDim = fitPars.size
//Возвращает производную k-того Eq по l-тому параметру
val res = Array(fitDim) { DoubleArray(fitDim) }
val sderiv = Array(source.dataSize) { DoubleArray(fitDim) }
/*
* Важно! Если не делать предварителього вычисления этих производных, то
* количество вызывов функции будет dim^2 вместо dim Первый индекс -
* номер точки, второй - номер переменной, по которой берется производная
*/
var i: Int// номер точки из набора данных
var k: Int// номер уравнения
var l: Int// номер параметра, по короторому берется производная
l = 0
while (l < fitDim) {
i = 0
while (i < source.dataSize) {
sderiv[i][l] = source.getDisDeriv(fitPars[l], i, set)
i++
}
l++
}
var summ: Double
k = 0
while (k < fitDim) {
l = 0
while (l < fitDim) {
summ = 0.0
i = 0
while (i < source.dataSize) {
// Тут баг, при нулевой дисперсии скатываемся в сингулярность.!!!
assert(weight.dispersion[i] > 0)
summ += sderiv[i][l] * weight.derivs[k][i] / weight.dispersion[i]
i++
}
res[k][l] = summ
//TODO Это правильно. Почему??
if (source.prior != null
&& source.prior.names.contains(fitPars[k])
&& source.prior.names.contains(fitPars[l])) {
val prior = source.prior
Logger.getAnonymousLogger().warning("QOW does not interpret prior probability correctly")
val pi = prior.value(set)
val deriv1 = prior.derivValue(fitPars[k], set)
val deriv2 = prior.derivValue(fitPars[l], set)
//считаем априорную вероятность независимой для разных переменных
res[k][l] += deriv1 * deriv2 / pi / pi
}
l++
}
k++
}
return Array2DRowRealMatrix(res)
}
/**
* Этот метод считает матрицу производных сразу в тета-0. Сильно экономит
* вызовы функции
*
* @param source
* @param fitPars
* @param weight
* @return
*/
fun getEqDerivValues(source: FitState, fitPars: Array<String>, weight: QOWeight): RealMatrix {
val fitDim = fitPars.size
val res = Array(fitDim) { DoubleArray(fitDim) }
var i: Int
var k: Int
var l: Int
var summ: Double
k = 0
while (k < fitDim) {
l = 0
while (l < fitDim) {
summ = 0.0
i = 0
while (i < source.dataSize) {
summ += weight.derivs[l][i] * weight.derivs[k][i] / weight.dispersion[i]
i++
}
res[k][l] = summ
//TODO Это правильно. Почему??
if (source.prior != null
&& source.prior.names.contains(fitPars[k])
&& source.prior.names.contains(fitPars[l])) {
Logger.getAnonymousLogger().warning("QOW does not interpret prior probability correctly")
val prior = source.prior
val pi = prior.value(weight.theta)
val deriv1 = prior.derivValue(fitPars[k], weight.theta)
val deriv2 = prior.derivValue(fitPars[l], weight.theta)
//считаем априорную вероятность независимой для разный переменных
res[k][l] += deriv1 * deriv2 / pi / pi
}
l++
}
k++
}
return Array2DRowRealMatrix(res)
}
fun getEqValues(source: FitState, set: ParamSet, weight: QOWeight): NamedVector {
return getEqValues(source, set, weight.namesAsArray(), weight)
}
/**
* Значения уравнений метода квазиоптимальных весов
*
* @param source
* @param set
* @param fitPars
* @param weight
* @return
*/
fun getEqValues(source: FitState, set: ParamSet, fitPars: Array<String>, weight: QOWeight): NamedVector {
val res = DoubleArray(fitPars.size)
var i: Int
var k: Int
var summ: Double
val diss = DoubleArray(source.dataSize)
i = 0
while (i < diss.size) {
diss[i] = source.getDis(i, set)
i++
}
k = 0
while (k < fitPars.size) {
summ = 0.0
i = 0
while (i < source.dataSize) {
summ += diss[i] * weight.derivs[k][i] / weight.dispersion[i]
i++
}
res[k] = summ
//Поправка на априорную вероятность
if (source.prior != null && source.prior.names.contains(fitPars[k])) {
Logger.getAnonymousLogger().warning("QOW does not interpret prior probability correctly")
val prior = source.prior
res[k] -= prior.derivValue(fitPars[k], set) / prior.value(set)
}
k++
}
return NamedVector(fitPars, res)
}
}

View File

@ -0,0 +1,5 @@
package ru.inr.mass.maths
import space.kscience.kmath.linear.Point
public typealias MultiFunction = (Point<Double>) -> Double

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 hep.dataforge.stat.fit.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,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 hep.dataforge.stat.fit.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 hep.dataforge.stat.fit.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 hep.dataforge.stat.fit.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,108 @@
/*
* 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 hep.dataforge.stat.fit.MINUITPlugin
import ru.inr.mass.minuit.*
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 hep.dataforge.stat.fit.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,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 hep.dataforge.stat.fit.MINUITPlugin
import ru.inr.mass.minuit.*
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,17 @@
package ru.inr.mass.maths
import org.apache.commons.math3.analysis.UnivariateFunction
import org.apache.commons.math3.analysis.interpolation.SplineInterpolator
import org.apache.commons.math3.analysis.polynomials.PolynomialSplineFunction
import kotlin.math.abs
public fun UnivariateFunction.cache(
range: ClosedFloatingPointRange<Double>,
numCachePoints: Int,
): PolynomialSplineFunction? {
val length = abs(range.endInclusive - range.start)
val grid: DoubleArray = DoubleArray(numCachePoints) { range.start + length / (numCachePoints - 1) * it }
val vals = DoubleArray(grid.size) { value(grid[it]) }
val interpolator = SplineInterpolator()
return interpolator.interpolate(grid, vals)
}

View File

@ -8,7 +8,7 @@ import space.kscience.dataforge.context.warn
import space.kscience.kmath.histogram.UnivariateHistogram
import space.kscience.kmath.histogram.center
import space.kscience.kmath.histogram.put
import space.kscience.kmath.structures.RealBuffer
import space.kscience.kmath.structures.DoubleBuffer
import space.kscience.kmath.structures.asBuffer
@ -21,8 +21,8 @@ fun NumassPoint.spectrum(): UnivariateHistogram = UnivariateHistogram.uniform(1.
}
}
operator fun UnivariateHistogram.component1(): RealBuffer = bins.map { it.domain.center }.toDoubleArray().asBuffer()
operator fun UnivariateHistogram.component2(): RealBuffer = bins.map { it.value }.toDoubleArray().asBuffer()
operator fun UnivariateHistogram.component1(): DoubleBuffer = bins.map { it.domain.center }.toDoubleArray().asBuffer()
operator fun UnivariateHistogram.component2(): DoubleBuffer = bins.map { it.value }.toDoubleArray().asBuffer()
fun Collection<NumassPoint>.spectrum(): UnivariateHistogram {
if (distinctBy { it.voltage }.size != 1) {

View File

@ -1,22 +1,19 @@
pluginManagement {
repositories {
maven("https://repo.kotlin.link")
gradlePluginPortal()
mavenCentral()
jcenter()
maven("https://dl.bintray.com/kotlin/kotlin-eap")
maven("https://dl.bintray.com/kotlin/kotlin-dev")
gradlePluginPortal()
}
val toolsVersion = "0.9.2"
val kotlinVersion = "1.4.31"
val toolsVersion = "0.9.3"
val kotlinVersion = "1.4.32"
plugins {
id("ru.mipt.npm.gradle.project") version toolsVersion
id("ru.mipt.npm.gradle.mpp") version toolsVersion
id("ru.mipt.npm.gradle.jvm") version toolsVersion
id("ru.mipt.npm.gradle.js") version toolsVersion
id("ru.mipt.npm.gradle.publish") version toolsVersion
kotlin("jvm") version kotlinVersion
kotlin("js") version kotlinVersion
}
@ -35,3 +32,4 @@ pluginManagement {
include("numass-data-model")
include("numass-data-proto")
include("numass-workspace")
include("numass-model")