#include "TFumiliMinimizer.h"
#include "Math/IFunction.h"
#include "Math/Util.h"
#include "TError.h"
#include "TFumili.h"
#include <iostream>
#include <cassert>
#include <algorithm>
#include <functional>
#ifdef USE_FUMILI_FUNCTION
bool gUseFumiliFunction = true;
#include "Fit/PoissonLikelihoodFCN.h"
#include "Fit/LogLikelihoodFCN.h"
#include "Fit/Chi2FCN.h"
#include "TF1.h"
#include "TFumili.h"
template<class MethodFunc>
class FumiliFunction : public ROOT::Math::FitMethodFunction {
typedef ROOT::Math::FitMethodFunction::BaseFunction BaseFunction;
public:
FumiliFunction(TFumili * fumili, const ROOT::Math::FitMethodFunction * func) :
ROOT::Math::FitMethodFunction(func->NDim(), func->NPoints() ),
fFumili(fumili),
fObjFunc(0)
{
fObjFunc = dynamic_cast<const MethodFunc *>(func);
assert(fObjFunc != 0);
fModFunc = new TF1("modfunc",ROOT::Math::ParamFunctor( &fObjFunc->ModelFunction() ) );
fFumili->SetUserFunc(fModFunc);
}
ROOT::Math::FitMethodFunction::Type_t Type() const { return fObjFunc->Type(); }
FumiliFunction * Clone() const { return new FumiliFunction(fFumili, fObjFunc); }
double DataElement(const double * , unsigned int i, double * g) const {
unsigned int npar = fObjFunc->NDim();
double y = 0;
double invError = 0;
const double *x = fObjFunc->Data().GetPoint(i,y,invError);
double fval = fFumili->EvalTFN(g,const_cast<double *>( x));
fFumili->Derivatives(g, const_cast<double *>( x));
if ( fObjFunc->Type() == ROOT::Math::FitMethodFunction::kLogLikelihood) {
double logPdf = y * ROOT::Math::Util::EvalLog( fval) - fval;
for (unsigned int k = 0; k < npar; ++k) {
g[k] *= ( y/fval - 1.) ;
}
return logPdf;
}
else if (fObjFunc->Type() == ROOT::Math::FitMethodFunction::kLeastSquare ) {
double resVal = (y-fval)*invError;
for (unsigned int k = 0; k < npar; ++k) {
g[k] *= -invError;
}
return resVal;
}
return 0;
}
private:
double DoEval(const double *x ) const {
return (*fObjFunc)(x);
}
TFumili * fFumili;
const MethodFunc * fObjFunc;
TF1 * fModFunc;
};
#else
bool gUseFumiliFunction = false;
#endif
ROOT::Math::FitMethodFunction * TFumiliMinimizer::fgFunc = 0;
ROOT::Math::FitMethodGradFunction * TFumiliMinimizer::fgGradFunc = 0;
TFumili * TFumiliMinimizer::fgFumili = 0;
ClassImp(TFumiliMinimizer)
TFumiliMinimizer::TFumiliMinimizer(int ) :
fDim(0),
fNFree(0),
fMinVal(0),
fEdm(-1),
fFumili(0)
{
#ifdef USE_STATIC_TMINUIT
if (fgFumili == 0) fgFumili = new TFumili(0);
fFumili = fgFumili;
#else
if (fFumili) delete fFumili;
fFumili = new TFumili(0);
fgFumili = fFumili;
#endif
}
TFumiliMinimizer::~TFumiliMinimizer()
{
if (fFumili) delete fFumili;
}
TFumiliMinimizer::TFumiliMinimizer(const TFumiliMinimizer &) :
Minimizer()
{
}
TFumiliMinimizer & TFumiliMinimizer::operator = (const TFumiliMinimizer &rhs)
{
if (this == &rhs) return *this;
return *this;
}
void TFumiliMinimizer::SetFunction(const ROOT::Math::IMultiGenFunction & func) {
fDim = func.NDim();
fFumili->SetParNumber(fDim);
const ROOT::Math::FitMethodFunction * fcnfunc = dynamic_cast<const ROOT::Math::FitMethodFunction *>(&func);
if (!fcnfunc) {
Error("SetFunction","Wrong Fit method function type used for Fumili");
return;
}
fgFunc = const_cast<ROOT::Math::FitMethodFunction *>(fcnfunc);
fgGradFunc = 0;
fFumili->SetFCN(&TFumiliMinimizer::Fcn);
#ifdef USE_FUMILI_FUNCTION
if (gUseFumiliFunction) {
if (fcnfunc->Type() == ROOT::Math::FitMethodFunction::kLogLikelihood)
fgFunc = new FumiliFunction<ROOT::Fit::PoissonLikelihoodFCN<ROOT::Math::FitMethodFunction::BaseFunction> >(fFumili,fcnfunc);
else if (fcnfunc->Type() == ROOT::Math::FitMethodFunction::kLeastSquare)
fgFunc = new FumiliFunction<ROOT::Fit::Chi2FCN<ROOT::Math::FitMethodFunction::BaseFunction> >(fFumili,fcnfunc);
}
#endif
}
void TFumiliMinimizer::SetFunction(const ROOT::Math::IMultiGradFunction & func) {
fDim = func.NDim();
fFumili->SetParNumber(fDim);
const ROOT::Math::FitMethodGradFunction * fcnfunc = dynamic_cast<const ROOT::Math::FitMethodGradFunction *>(&func);
if (!fcnfunc) {
Error("SetFunction","Wrong Fit method function type used for Fumili");
return;
}
fgFunc = 0;
fgGradFunc = const_cast<ROOT::Math::FitMethodGradFunction *>(fcnfunc);
fFumili->SetFCN(&TFumiliMinimizer::Fcn);
}
void TFumiliMinimizer::Fcn( int & , double * g , double & f, double * x , int ) {
f = TFumiliMinimizer::EvaluateFCN(const_cast<double*>(x),g);
}
double TFumiliMinimizer::EvaluateFCN(const double * x, double * grad) {
double sum = 0;
unsigned int ndata = 0;
unsigned int npar = 0;
if (fgFunc) {
ndata = fgFunc->NPoints();
npar = fgFunc->NDim();
fgFunc->UpdateNCalls();
}
else if (fgGradFunc) {
ndata = fgGradFunc->NPoints();
npar = fgGradFunc->NDim();
fgGradFunc->UpdateNCalls();
}
std::vector<double> gf(npar);
std::vector<double> hess(npar*(npar+1)/2);
for (unsigned int ipar = 0; ipar < npar; ++ipar)
grad[ipar] = 0;
#ifdef DEBUG
std::cout << "=============================================";
std::cout << "par = ";
for (unsigned int ipar = 0; ipar < npar; ++ipar)
std::cout << x[ipar] << "\t";
std::cout << std::endl;
if (fgFunc) std::cout << "type " << fgFunc->Type() << std::endl;
#endif
if ( (fgFunc && fgFunc->Type() == ROOT::Math::FitMethodFunction::kLeastSquare) ||
(fgGradFunc && fgGradFunc->Type() == ROOT::Math::FitMethodGradFunction::kLeastSquare) ) {
double fval = 0;
for (unsigned int i = 0; i < ndata; ++i) {
if (gUseFumiliFunction) {
fval = fgFunc->DataElement( x, i, &gf[0]);
}
else {
if (fgFunc != 0)
fval = fgFunc->DataElement(x, i, &gf[0]);
else
fval = fgGradFunc->DataElement(x, i, &gf[0]);
}
sum += fval*fval;
for (unsigned int j = 0; j < npar; ++j) {
grad[j] += fval * gf[j];
for (unsigned int k = j; k < npar; ++ k) {
int idx = j + k*(k+1)/2;
hess[idx] += gf[j] * gf[k];
}
}
}
}
else if ( (fgFunc && fgFunc->Type() == ROOT::Math::FitMethodFunction::kLogLikelihood) ||
(fgGradFunc && fgGradFunc->Type() == ROOT::Math::FitMethodGradFunction::kLogLikelihood) ) {
double fval = 0;
for (unsigned int i = 0; i < ndata; ++i) {
if (gUseFumiliFunction) {
fval = fgFunc->DataElement( x, i, &gf[0]);
}
else {
if (fgFunc != 0)
fval = fgFunc->DataElement(x, i, &gf[0]);
else
fval = fgGradFunc->DataElement(x, i, &gf[0]);
}
sum -= fval;
for (unsigned int j = 0; j < npar; ++j) {
double gfj = gf[j];
grad[j] -= gfj;
for (unsigned int k = j; k < npar; ++ k) {
int idx = j + k*(k+1)/2;
hess[idx] += gfj * gf[k];
}
}
}
}
else {
Error("EvaluateFCN"," type of fit method is not supported, it must be chi2 or log-likelihood");
}
double * zmatrix = fgFumili->GetZ();
double * pl0 = fgFumili->GetPL0();
assert(zmatrix != 0);
assert(pl0 != 0);
unsigned int k = 0;
unsigned int l = 0;
for (unsigned int i = 0; i < npar; ++i) {
for (unsigned int j = 0; j <= i; ++j) {
if (pl0[i] > 0 && pl0[j] > 0) {
zmatrix[l++] = hess[k];
}
k++;
}
}
#ifdef DEBUG
std::cout << "FCN value " << sum << " grad ";
for (unsigned int ipar = 0; ipar < npar; ++ipar)
std::cout << grad[ipar] << "\t";
std::cout << std::endl << std::endl;
#endif
return 0.5*sum;
}
bool TFumiliMinimizer::SetVariable(unsigned int ivar, const std::string & name, double val, double step) {
if (fFumili == 0) {
Error("SetVariableValue","invalid TFumili pointer. Set function first ");
return false;
}
#ifdef DEBUG
std::cout << "set variable " << ivar << " " << name << " value " << val << " step " << step << std::endl;
#endif
int ierr = fFumili->SetParameter(ivar , name.c_str(), val, step, 0., 0. );
if (ierr) {
Error("SetVariable","Error for parameter %d ",ivar);
return false;
}
return true;
}
bool TFumiliMinimizer::SetLimitedVariable(unsigned int ivar, const std::string & name, double val, double step, double lower, double upper) {
if (fFumili == 0) {
Error("SetVariableValue","invalid TFumili pointer. Set function first ");
return false;
}
#ifdef DEBUG
std::cout << "set limited variable " << ivar << " " << name << " value " << val << " step " << step << std::endl;
#endif
int ierr = fFumili->SetParameter(ivar, name.c_str(), val, step, lower, upper );
if (ierr) {
Error("SetLimitedVariable","Error for parameter %d ",ivar);
return false;
}
return true;
}
#ifdef LATER
bool Fumili2Minimizer::SetLowerLimitedVariable(unsigned int ivar , const std::string & name , double val , double step , double lower ) {
double s = val-lower;
double upper = s*1.0E15;
if (s != 0) upper = 1.0E15;
return SetLimitedVariable(ivar, name, val, step, lower,upper);
}
#endif
bool TFumiliMinimizer::SetFixedVariable(unsigned int ivar, const std::string & name, double val) {
if (fFumili == 0) {
Error("SetVariableValue","invalid TFumili pointer. Set function first ");
return false;
}
int ierr = fFumili->SetParameter(ivar, name.c_str(), val, 0., val, val );
fFumili->FixParameter(ivar);
#ifdef DEBUG
std::cout << "Fix variable " << ivar << " " << name << " value " << std::endl;
#endif
if (ierr) {
Error("SetFixedVariable","Error for parameter %d ",ivar);
return false;
}
return true;
}
bool TFumiliMinimizer::SetVariableValue(unsigned int ivar, double val) {
if (fFumili == 0) {
Error("SetVariableValue","invalid TFumili pointer. Set function first ");
return false;
}
TString name = fFumili->GetParName(ivar);
double oldval, verr, vlow, vhigh = 0;
int ierr = fFumili->GetParameter( ivar, &name[0], oldval, verr, vlow, vhigh);
if (ierr) {
Error("SetVariableValue","Error for parameter %d ",ivar);
return false;
}
#ifdef DEBUG
std::cout << "set variable " << ivar << " " << name << " value "
<< val << " step " << verr << std::endl;
#endif
ierr = fFumili->SetParameter(ivar , name , val, verr, vlow, vhigh );
if (ierr) {
Error("SetVariableValue","Error for parameter %d ",ivar);
return false;
}
return true;
}
bool TFumiliMinimizer::Minimize() {
if (fFumili == 0) {
Error("SetVariableValue","invalid TFumili pointer. Set function first ");
return false;
}
fgFumili = fFumili;
double arglist[10];
int printlevel = PrintLevel();
if (printlevel == 0) fFumili->ExecuteCommand("SET NOW",arglist,0);
else fFumili->ExecuteCommand("SET WAR",arglist,0);
arglist[0] = MaxFunctionCalls();
arglist[1] = Tolerance();
if (printlevel > 0)
std::cout << "Minimize using TFumili with tolerance = " << Tolerance()
<< " max calls " << MaxFunctionCalls() << std::endl;
int iret = fFumili->ExecuteCommand("MIGRAD",arglist,2);
fStatus = iret;
int ntot;
int nfree;
double errdef = 0;
fFumili->GetStats(fMinVal,fEdm,errdef,nfree,ntot);
if (printlevel > 0)
fFumili->PrintResults(printlevel,fMinVal);
assert (static_cast<unsigned int>(ntot) == fDim);
assert( nfree == fFumili->GetNumberFreeParameters() );
fNFree = nfree;
fParams.resize( fDim);
fErrors.resize( fDim);
fCovar.resize(fDim*fDim);
const double * cv = fFumili->GetCovarianceMatrix();
unsigned int l = 0;
for (unsigned int i = 0; i < fDim; ++i) {
fParams[i] = fFumili->GetParameter( i );
fErrors[i] = fFumili->GetParError( i );
if ( !fFumili->IsFixed(i) ) {
for (unsigned int j = 0; j <=i ; ++j) {
if ( !fFumili->IsFixed(j) ) {
fCovar[i*fDim + j] = cv[l];
fCovar[j*fDim + i] = fCovar[i*fDim + j];
l++;
}
}
}
}
return (iret==0) ? true : false;
}