Difference of convergence for Minuit2 between a portable and a server

Hi Lorenzo,

We now have a stable version on both the server and the portable. The issue was related to a set of parameters that could not be fitted. It was OK on the server but not on the portable (or vice versa!). We put a statistical test for removing these parameters.

You can close this thread as solved…other questions are coming in a new thread.

Thank you so much for the gang at CERN. You kick ass !

Cheers,

ThierryA

@moneta I have a related problem (different Minuit2 result on different computers), but I’m having trouble debugging it using Minuit2 print level.
On my laptop I have root v6-20-04-23-g95ac5da, while on another machine I have root v6-24-02-2-g0173f51a8c.
I’m using ROOT::Fit::Fitter with a custom chi2 class.
On my laptop, if I do fitter.Config().MinimizerOptions().SetPrintLevel(3); I get the usual detailed output from VariableMetricBuilder at each iteration, while on the other machine I get nothing.
Indeed, I see that in root 6-24 the print logic changed with respect to root 6-20.
I tried to use MnPrint::SetGlobalLevel(3) on the other machine, but it didn’t help.
What is the corresponding line of code to minimize.Minimizer().Builder().SetPrintLevel(3) if I use ROOT::Fit::Fitter instead of MnMinimize?

Hi
The line to change the Minuit print level, if using the Fitter class, should be:

 fitter.Config().MinimizerOptions().SetPrintLevel(3);

I attach here an example of a fit using the Fitter class

/// Example of unbinned fit using ROOT

std::vector<double> GenerateData(int nevts)  {
   // generate 30% with gaussian (signal)
   // and 70% with background (exponential)

   std::vector<double> v(nevts);


   // use in range [0,10]
   for ( auto & e : v) {
         if (gRandom->Rndm() < 0.3) 
            e = gRandom->Gaus(5,0.5);
         else {
            // take into account some events are outside the range
            do { 
               e = gRandom->Exp(10);
            } while (e > 10);
         }
   }
   return v;

}

int UnbinFit(int printLevel=0) {

   const int nevts = 5000;

   auto x = GenerateData(nevts);

   // make the dataset for fitting
   ROOT::Fit::UnBinData data(x.size());
   for ( auto & val : x)
      data.Add(val);

   // create fit function

   auto f1 = new TF1("f1","[fs]*ROOT::Math::normal_pdf(x,[sigma],[mean]) + (1.-[fs])*ROOT::Math::exponential_pdf(x,[tau])/ROOT::Math::exponential_cdf(10,[tau])",0,10);
   
   // set initial fit parameters
   f1->SetParameter("fs",0.5);
   f1->SetParameter("mean",4);
   f1->SetParameter("sigma",1);
   f1->SetParameter("tau",0.2);
   
   
   f1->Print("V");

   f1->Draw();

   ROOT::Fit::Fitter fitter;

   
   ROOT::Math::WrappedMultiTF1 wf(*f1);
   // pass false do not use gradients of TF1 when fitting
   fitter.SetFunction(wf, false);

   fitter.Config().MinimizerOptions().SetMinimizerType("Minuit2");
   fitter.Config().MinimizerOptions().SetPrintLevel(printLevel);

   bool ret = fitter.Fit(data);
   if (ret)
      fitter.Result().Print(std::cout);
   else {
      std::cout << "Unbinned Likelihood Fit Failed " << std::endl;
      return -1;
   }
   // plot the data in an histogram
   auto h1 = new TH1D("h1","h1",100,0,10);
   h1->FillN(nevts,x.data(), nullptr);

   // plot the data and the function on top normalized to the obsered data
   auto normFunc = new TF1("normFunc","[Constant]*f1",0,10);
   normFunc->SetParameter("Constant",h1->GetBinWidth(1)*nevts);
   h1->GetListOfFunctions()->Add(normFunc);
   h1->Draw();

   return 0;
}

Best regards

Lorenzo

Thanks for your reply.
Your macro work as expected on both my machines.

I’m setting up the fitter object in a similar way:

ROOT::Fit::Fitter fitter;
fitter.Config().MinimizerOptions().SetMinimizerType("Minuit2");
fitter.Config().MinimizerOptions().SetErrorDef(1);
fitter.Config().MinimizerOptions().SetMaxIterations(1e6);
fitter.Config().MinimizerOptions().SetMaxFunctionCalls(1e6);
fitter.Config().MinimizerOptions().SetTolerance(0.001);
fitter.Config().SetNormErrors();
fitter.Config().MinimizerOptions().SetPrintLevel(3);
fitter.Config().MinimizerOptions().Print();

vector<ROOT::Fit::ParameterSettings> params;
/* 
code to setup initial parameters and limits
*/
fitter.Config().SetParamsSettings(params);

// create custom chi2 object and fit
GlobalChi2 global_chi2(data, model);
fitter.FitFCN(params.size(), global_chi2, 0, global_chi2.DataSize(), true);

but on the machine with root 6-24 I only get this:

            Minimizer Type :         Minuit2
      Minimizer Algorithm :          Migrad
                 Strategy :               1
                Tolerance :           0.001
           Max func calls :         1000000
           Max iterations :         1000000
           Func Precision :              -1
         Error definition :               1
              Print Level :               3
Minuit2Minimizer: Minimize with max-calls 1000000 convergence for edm < 0.001 strategy 1
Minuit2Minimizer : Valid minimum - status = 0
FVAL  = 43.5980847001244314
Edm   = 1.24330123599982261e-07
Nfcn  = 1266

while on my laptop with root 6-20 I get the full output:

           Minimizer Type :         Minuit2
      Minimizer Algorithm :          Migrad
                 Strategy :               1
                Tolerance :           0.001
           Max func calls :         1000000
           Max iterations :         1000000
           Func Precision :              -1
         Error definition :               1
              Print Level :               3
Minuit2Minimizer: Minimize with max-calls 1000000 convergence for edm < 0.001 strategy 1
MnSeedGenerator: for initial parameters FCN = 17021.31610362
MnSeedGenerator: Initial state:   - FCN =   17021.31610362 Edm =      1017.21 NCalls =     21
MnSeedGenerator: Negative G2 found - new state:   - FCN =   11829.60496857 Edm =      2345.71 NCalls =     52
VariableMetric: start iterating until Edm is < 2e-06
VariableMetric: Initial state   - FCN =   11829.60496857 Edm =      2345.71 NCalls =     52
VariableMetric: Iteration #   0 - FCN =   11829.60496857 Edm =      2345.71 NCalls =     52
VariableMetric: Iteration #   1 - FCN =   10138.56120052 Edm =      8143.11 NCalls =     77

and so on.

I guess there must be some strange interplay between Minuit2 printing logic and the rest of my program…

It could be that you have re-defined, maybe without knowing it, the global Error level of ROOT.
Can you print , before calling fitter.FitFCN the ROOT variable gErrorIgnoreLevel.
It should be -1 by default, if it has an higher value (e.g. >=1000) Info messages are suppressed

Lorenzo

1 Like

That was it, I had gErrorIgnoreLevel = kWarning.
Thanks a lot!