19 #include <TDecompSVD.h> 
   24 #include "Math/VectorUtil.h" 
   25 #include "Math/Minimizer.h" 
   26 #include "Math/Factory.h" 
   27 #include "Math/Functor.h" 
   33 static const char * 
green = 
"\x1B[32m",
 
   38 static const auto eps = std::numeric_limits<double>::epsilon();
 
   39 static const auto inf = std::numeric_limits<double>::infinity();
 
   49     std::vector<double> 
X; 
 
   50     size_t degree ()
 const { 
return X.size()-1; } 
 
   64     template<
typename POL>
 
   65     TF1 * 
GetTF1 (
const char * 
name, POL pol, 
double m, 
double M)
 const {
 
   67         auto f = 
new TF1(
name, pol, m, M, pol.npars);
 
   68         f->SetParameters(
X.data());
 
   69         f->SetTitle(Form(
"#chi^{2}_{%zu}/ndf = %.1f/%d", 
degree(), 
chi2, 
ndf));
 
   80         stream << 
bold << 
"Chi2/ndf = " << result.
chi2 << 
" / " << result.
ndf 
   81                             << 
" = " << result.
x2n() << 
"\u00B1" << result.
x2nErr()
 
   82                             << 
" (prob = " << result.
prob() << 
")\n" << 
def;
 
   83         size_t nrep = result.
chi2sT.size();
 
   84         assert(nrep == result.
chi2sV.size());
 
   85         stream << result.
X.size() << 
" parameters: ";
 
   86         for (
auto x: result.
X) stream << std::setw(12) << x;
 
   89             stream << nrep <<   
" training replicas: Chi2T/" <<  result.
ndf                    << 
" = " << result.
x2nT() << 
'\n' 
   90                    << nrep << 
" validation replicas: Chi2V/" << (result.
ndf + result.
X.size()) << 
" = " << result.
x2nV() << 
'\n';
 
   92     else stream << 
"Not a fit\n";
 
   96 static std::deque<Result> 
chi2s; 
 
  110     if (!former.
valid || !current.
valid) 
throw -1.;
 
  111     double RSS1 = former.
chi2,
 
  113     size_t p1 = former.
X.size(),
 
  114            p2 = current.
X.size();
 
  116     int ndf = current.
ndf; 
 
  117     double x = ((RSS1-RSS2)/(p2-p1))/(RSS2/ndf);
 
  118     double pvalue = TMath::FDistI(x, p2-p1, ndf);
 
  131     size_t nrep = former.
chi2sV.size();
 
  132     if (nrep == 0) 
return 0;
 
  134     int Nf = former .
ndf + former .
X.size(),
 
  135         Nc = current.
ndf + current.
X.size();
 
  137     for (
size_t i = 0; i < nrep; ++i)
 
  147                     std::ostream& stream = std::cout, 
size_t best = 0)
 
  152     for (
auto result = begin(
chi2s); result != end(
chi2s); ++result)
 
  153         if (result->valid) ++nGoodFits;
 
  155         stream << 
red << 
"Only one valid fit. No test to run.\n" << 
def;
 
  158     stream << 
"   " << 
bold;
 
  159     for (
auto result = begin(
chi2s); result != prev(end(
chi2s)); ++result) {
 
  161         size_t deg = result->degree();
 
  162         if (deg == best) stream << 
green;
 
  163         if (!result->valid) stream << 
red;
 
  164         stream << setw(10) << deg << 
def;
 
  167     for (
auto result2 = next(begin(
chi2s)); result2 != end(
chi2s); ++result2) {
 
  169         size_t deg2 = result2->degree();
 
  170         if (deg2 == best) stream << 
green;
 
  171         if (!result2->valid) stream << 
red;
 
  172         stream << setw(3) << deg2 << 
def;
 
  173         if (deg2 == best) stream << 
def;
 
  174         stream << setprecision(4);
 
  175         for (
auto result1 = begin(
chi2s); result1 != result2; ++result1) {
 
  177             try            { stream << setw(10) << test(*result1, *result2); }
 
  178             catch (
double) { stream << 
red << 
"         -" << 
def; }
 
  180         stream << 
bold << 
'\n';
 
  203     FunctionalForm (
const char * classname, 
unsigned int Npars, 
double mi, 
double Ma, std::ostream& Stream = std::cout) :
 
  207             stream << 
"Declaring a `" << classname << 
"` with " << 
npars << 
" parameters between " << mi << 
" and " << Ma << 
'\n';
 
  216     virtual double operator() (
const double *x, 
const double *
p) 
const = 0;
 
  226         if (std::abs(x) > 1) 
return 0.;
 
  238         if (std::abs(x) > 1) 
return 0.;
 
  258     Polynomial (
unsigned int d, 
double mi, 
double Ma, std::ostream& Stream = std::cout) :
 
  265     double operator() (
const double *x, 
const double *
p)
 const override 
  267         double nx = -1 + 2* (Log(*x) - Log(m)) / (Log(M) - Log(m));
 
  269         for (
unsigned int i = 0; i < npars; ++i)
 
  270             result += 
p[i]*T(nx,i);
 
  277 template<
typename FunctionalForm>
 
  303             std::ostream& Stream = std::cout 
 
  305         n(iM-im+1), c(n), e(n+1), v(n), w(n), s(n), cov(n,n), rot(n,n), invcov(n,n), 
f(ff), stream(Stream)
 
  312             stream << 
"n = " << n << 
'\n';
 
  315             stream << 
"Checking histogram\n";
 
  316         for (
int i = im; i <= iM; ++i) {
 
  317             auto content = h->GetBinContent(i);
 
  319                 stream << setw(3) << i
 
  320                        << setw(12) << h->GetBinCenter(i)
 
  321                        << setw(12) << content << 
'\n';
 
  322             assert(isnormal(content));
 
  325         if (hcov != 
nullptr) {
 
  327                 stream << 
"Checking covariance\n";
 
  328             for (
int i = im; i <= iM; ++i)
 
  329             for (
int j = im; j <= iM; ++j) {
 
  330                 auto content = hcov->GetBinContent(i,j);
 
  332                     stream << setw(12) << content;
 
  333                     if (j == iM) stream << 
'\n';
 
  335                 assert(isfinite(content));
 
  343             stream << 
"Converting from histograms to vector and matrix (shift = " << shift << 
"):";
 
  344         for (
int i = 0; i < n; ++i) {
 
  345             e(i) = h->GetBinLowEdge(i+shift);
 
  346             c(i) = h->GetBinCenter (i+shift);
 
  347             v(i) = h->GetBinContent(i+shift);
 
  349                 stream << 
'\n' << setw(5) << i << setw(10) << c(i) << setw(15) << v(i) << setw(15);
 
  350             assert(abs(v(i)) > 0);
 
  351             for (
int j = 0; j < n; ++j) {
 
  352                 cov(i,j) = hcov != 
nullptr ? hcov->GetBinContent(i+shift,j+shift)
 
  353                                   : i == j ? pow(h->GetBinError(i+shift),2) : 0;
 
  354                 if (i == j && 
verbose) stream << (100*sqrt(cov(i,j))/v(i)) << 
'%';
 
  357         e(n) = h->GetBinLowEdge(n+shift);
 
  361         if (!cov.IsSymmetric()) {
 
  363                 stream << 
"Forcing to be symmetric\n";
 
  369             assert(cov.IsSymmetric());
 
  374         rot = cov.EigenVectors(s); 
 
  376             stream << 
"Getting eigenvalues, expect different columns only in case of non-zero bin-to-bin correlations:";
 
  377         for (
int k = 0; k < n; ++k) {
 
  379                 stream << 
'\n' << setw(5) << k << setw(15) << s[k] << setw(15) << cov(k,k);
 
  388             stream << 
"Inverting...\n";
 
  392         invcov = svd.Invert(status);
 
  396             stream << 
"Ready to fit\n";
 
  401     double operator() (
const double * 
p)
 
  404         for (
int i = 0; i < n; ++i) {
 
  405             auto y0 = 
f(&e(i), 
p),
 
  408             r(i) -= (y0+4*y1+y2)/6;
 
  410         return r * (invcov * r);
 
  418         for (
int k = 0; k < n; ++k)
 
  419             d(k) = r.Gaus(0, s(k));
 
  449         unsigned int maxdegree, 
 
  451         double stopParam = 1, 
 
  453         std::ostream& stream = std::cout 
 
  458     assert(h != 
nullptr);
 
  461         stream << 
"im = " << im << 
", iM = " << iM << 
'\n';
 
  463     unsigned int nbins = iM-im+1;
 
  465         stream << 
"nbins = " << nbins << 
'\n';
 
  468     double m = h->GetBinLowEdge(im),
 
  469            M = h->GetBinLowEdge(iM+1);
 
  471         stream << 
"m = " << m << 
", M = " << M << 
'\n';
 
  475     ROOT::Math::Functor functor(cor, pol.
npars);
 
  477     auto minimiser = ROOT::Math::Factory::CreateMinimizer(
"Minuit2", 
"Migrad");
 
  478     const int Ncalls = 1e6;
 
  479     minimiser->SetMaxFunctionCalls(Ncalls);
 
  480     minimiser->SetFunction(functor);
 
  481     minimiser->SetTolerance(0.001);
 
  482     minimiser->SetPrintLevel(
verbose);
 
  484     double fm = Log(h->GetBinContent(im)),
 
  485            fM = Log(h->GetBinContent(iM));
 
  487     double p0 = (fM+fm)/2, p1 = (fM-fm)/2;
 
  488     minimiser->SetVariable(0,
"p0",p0,
step);
 
  489     minimiser->SetVariable(1,
"p1",p1,
step);
 
  490     for (
unsigned int d = 2; d <= maxdegree; ++d) {
 
  491         minimiser->SetVariable(d,Form(
"p%d", d),0.,
step);
 
  492         minimiser->FixVariable(d);
 
  497     chi2s.push_back({
false, 
inf, nbins-2, {p0,p1}});
 
  500     while (
chi2s.size() <= maxdegree) {
 
  501         unsigned int degree = 
chi2s.size();
 
  502         minimiser->ReleaseVariable(degree);
 
  503         bool valid = minimiser->Minimize();
 
  505         const double * X = minimiser->X();
 
  506         assert(X != 
nullptr);
 
  507         double chi2 = cor(X);
 
  509         assert(isnormal(chi2));
 
  510         unsigned int npars = minimiser->NFree(),
 
  512         assert(npars == degree+1);
 
  514         const auto& former = 
chi2s.back();
 
  515         chi2s.push_back({valid, chi2, ndf, vector<double>(X,X+npars)});
 
  516         auto& current = 
chi2s.back();
 
  518         switch (minimiser->Status()) {
 
  519             case 0:  stream << current;
 
  522                          bool plateau = current.x2n() > former.x2n(),
 
  523                               goodchi2 = abs(current.x2n()-1) <= stopParam*current.x2nErr();
 
  525                              stream << 
red << 
"Chi2/ndf has increased.\n" << 
def;
 
  527                              stream << 
green << 
"Chi2 ~ ndf.\n" << 
def;
 
  533                              ftestRes = 
Ftest(former, current);
 
  534                              stream << 
"F-test result: " << ftestRes << 
'\n';
 
  536                          catch (
double) { stream << 
red << 
"No F-test possible.\n" << 
def; }
 
  537                          bool fvalid = ftestRes > stopParam; 
 
  542                              current.chi2sT.reserve(nrep);
 
  543                              current.chi2sV.reserve(nrep);
 
  544                              for (UInt_t seed = 1; seed <= nrep; ++seed) {
 
  546                                  for (
unsigned int i = 0; i <= degree; ++i)
 
  547                                      minimiser->SetVariableValue(i, current.X[i]);
 
  548                                  const double * X = minimiser->X();
 
  549                                  assert(X != 
nullptr);
 
  550                                  current.chi2sT.push_back(cor(X));
 
  552                                  current.chi2sV.push_back(cor(X));
 
  555                              for (
unsigned int i = 0; i <= degree; ++i)
 
  556                                  minimiser->SetVariableValue(i, current.X[i]);
 
  558                              xvalFrac = 
Xval(current, former);
 
  560                          bool xvalid = 1.-xvalFrac > stopParam;
 
  562                              stream << 
"Fraction of validation replicas with better fit Chi2/ndf = " << (xvalid ? 
green : 
red) << xvalFrac << 
def << 
'\n';
 
  568                                  found = plateau || goodchi2;
 
  583             case 1:  stream << 
red << 
"Warning: Fit parameter covariance was made positive defined\n" << 
def;
 
  584                      for (
unsigned int i = 0; i < npars; ++i) {
 
  585                          for (
unsigned int j = 0; j < npars; ++j)
 
  586                              stream << setw(12) << minimiser->CovMatrix(i,j);
 
  590             case 2:  stream << 
red << 
"Warning: Hesse is invalid\n" << 
def;
 
  592                          double * hessian = 
nullptr;
 
  593                          minimiser->GetHessianMatrix(hessian);
 
  594                          assert(hessian != 
nullptr);
 
  595                          for (
unsigned int i = 0; i < npars; ++i) {
 
  596                              for (
unsigned int j = 0; j < npars; ++j)
 
  597                                  stream << setw(12) << hessian[i*pol.
npars + j];
 
  602             case 3:  stream << 
red << 
"Warning: Expected Distance reached from the Minimum (EDM = " << minimiser->Edm() << 
") is above max\n" << 
def;
 
  604             case 4:  stream << 
red << 
"Warning: Reached call limit (Ncalls = " << Ncalls << 
")\n" << 
def;
 
  607             default: stream << 
red << 
"Warning: Unknown failure\n" << 
def;
 
  613             stream << 
"Correlations of the fit parameters:\n";
 
  614             for (
unsigned int i = 0; i < npars; ++i) {
 
  615                 for (
unsigned int j = 0; j < npars; ++j)
 
  616                     stream << setw(12) << minimiser->Correlation(i,j);
 
  621         if (current.ndf == 2) {
 
  622             stream << 
red << 
"Only 2 d.o.f. left. Stopping.\n" << 
def;
 
  630     const char * 
name = Form(
"smooth_%s", h->GetName());
 
  632     stream << 
"Best result: " << 
f->GetTitle() << 
"\n-----\n";
 
  634     stream << 
bold << 
"Running F-test:\n" << 
def;
 
  638         stream << 
bold << 
"Running Cross-validation:\n" << 
def;
 
  652         std::ostream& stream = std::cout)
 
  654     int im = 1, iM = h->GetNbinsX();
 
  655     return GetSmoothFit<T,Log,Exp>(h, cov, im, iM,
 
  656             maxdegree, criterion, stopParam, nrep, stream);
 
  666         std::ostream& stream = std::cout)
 
  668     return GetSmoothFit<T,Log,Exp>(h, 
nullptr, im, iM,
 
  669             maxdegree, criterion, stopParam, nrep, stream);
 
  680         std::ostream& stream = std::cout)
 
  682     return GetSmoothFit<T,Log,Exp>(h, 
nullptr,
 
  683             maxdegree, criterion, stopParam, nrep, stream);