Fit multiple histograms with the same function

Hi Sebastian,

if you change the parameter setting you need to pass a NULL pointer of parameter values to the method FitFCN.
Do:

 fitter.FitFCN(6,globalChi2,(double*) 0,dataB.Size()+dataSB.Size(),

I will update the doc to describe this

Lorenzo

The example does not work here with levenberg marquadt fitting. Is there a way to make such a simultanous fit with this algorithm?

I set lm fitting with:

	ROOT::Math::MinimizerOptions::SetDefaultMinimizer("GSLMultiFit", "");

then when I start the code, I get:

GSLNLSMinimizer: Invalid function set - only Chi2Func supported
Error in ROOT::Math::GSLNLSMinimizer::Minimize: Function has not been set
Error: Empty FitResult !

Hi,

If you use GSLMultiFit (L.M.) or Fumili and Fumili2, you need to implement a more complex interface. It is required you provide the single element (residual) of the chi2 square function. This is implemented in the class
ROOT::Fit::Chi2Function, but not for the global chi2 class which is pass to the Fitter.
Attached is an updated version of the macro which provides a global chi2 function implementing the required method for these fitters

Best Regards

Lorenzo
combineFit2.C (5.4 KB)

in my root 5.27/07 from svn (not the latest, but not that old), this does not work:

root [0] .x combineFit2.C+
Info in <TUnixSystem::ACLiC>: creating shared library /home/lewhoo/dr_scripts/combined_fit/./combineFit2_C.so
/home/lewhoo/dr_scripts/combined_fit/./combineFit2.C: In function ‘void combinedFit2()’:
/home/lewhoo/dr_scripts/combined_fit/./combineFit2.C:162: error: no matching function for call to ‘ROOT::Fit::Fitter::FitFCN(GlobalChi2&, int, unsigned int, bool)’
/opt/pi/ext/dload/root/include/Fit/Fitter.h:172: note: candidates are: bool ROOT::Fit::Fitter::FitFCN(const ROOT::Math::IMultiGenFunction&, const double*, unsigned int)
/opt/pi/ext/dload/root/include/Fit/Fitter.h:180: note:                 bool ROOT::Fit::Fitter::FitFCN(const ROOT::Math::IMultiGradFunction&, const double*, unsigned int)
/opt/pi/ext/dload/root/include/Fit/Fitter.h:188: note:                 bool ROOT::Fit::Fitter::FitFCN(void (*)(int&, double*, double&, double*, int))
/home/lewhoo/dr_scripts/combined_fit/./combineFit2.C:171: error: ‘class TF1’ has no member named ‘SetFitResult’
/home/lewhoo/dr_scripts/combined_fit/./combineFit2.C:172: error: no match for call to ‘(ROOT::Fit::DataRange) ()’
/opt/pi/ext/dload/root/include/Fit/DataRange.h:97: note: candidates are: std::pair<double, double> ROOT::Fit::DataRange::operator()(unsigned int) const
/home/lewhoo/dr_scripts/combined_fit/./combineFit2.C:172: error: no match for call to ‘(ROOT::Fit::DataRange) ()’
/opt/pi/ext/dload/root/include/Fit/DataRange.h:97: note: candidates are: std::pair<double, double> ROOT::Fit::DataRange::operator()(unsigned int) const
/home/lewhoo/dr_scripts/combined_fit/./combineFit2.C:177: error: ‘class TF1’ has no member named ‘SetFitResult’
/home/lewhoo/dr_scripts/combined_fit/./combineFit2.C:178: error: no match for call to ‘(ROOT::Fit::DataRange) ()’
/opt/pi/ext/dload/root/include/Fit/DataRange.h:97: note: candidates are: std::pair<double, double> ROOT::Fit::DataRange::operator()(unsigned int) const
/home/lewhoo/dr_scripts/combined_fit/./combineFit2.C:178: error: no match for call to ‘(ROOT::Fit::DataRange) ()’
/opt/pi/ext/dload/root/include/Fit/DataRange.h:97: note: candidates are: std::pair<double, double> ROOT::Fit::DataRange::operator()(unsigned int) const
g++: /home/lewhoo/dr_scripts/combined_fit/combineFit2_C_ACLiC_dict.o: No such file or directory
Error in <ACLiC>: Compilation failed!

just trying to find out how to cast the GlobalChi2 function :slight_smile:

Btw. haven’t checked that yet, but would it be possible to do in the same way a LogLikelihood fit?

Hi,

you should updated to a newer version. A loglikelihood fit will work as well, but not using GSLMultiFit, this is only for chi2 fits. You can use instead Fumili or Fumili2 but you need to implement in the GlobalChi2 class the Type() method to return a kLogLikelihood instead of a kLeastSquare

Lorenzo

Just compiling a newer version.

Now a strange question: I was using (standard) GSLMultiFit with loglikelihood (via ->Fit(“L”)) with results that seemed to be reasonable and different than chi2. The “reasonability” seems to be strange, since it is not supposed to work. Any idea, what the fitter was actually doing?

Hi,

it will work if your function to minimized for the fitting is defined as - 2 * Log L where L is the likelihood function. When using TH1::Fit with option L the function to minimize is simply logL, so I am very surprised it works when using GSLMultiFit. It will search for a maximum instead of a minimum, due to the opposite sign.
Best Regards
Lorenzo

Hi, again,

was the replacement of ROOT::Math::IMultiGenFunction by ROOT::Math::FitMethodFunction and the restructuring inside the GlobalChi2 of the given example only to allow using also GSLMultiFit and Fumili as minimizers or would it be also required in order to perform a logLikelihood fit with Minuit? In other words: What would be the most simple way for switching to a logLikelihood in the combinedFit.C example posted on Nov 12? Maybe it is obvious / trivial but I was not sure and did not want to implement something that might give reasonable results only by accident.

Thanks in advance!

Best regards,
Sebastian

Hi,

This changes are required in order to use Fumili or GSLMultiFit

For performing a binned log-likelihood fit you need just to use the ROOT::Fit::PoissonLLFunction class instead of the ROOT::Fit::Chi2Function class.
I attached the modified macro in this case.

Note, that the original combinedFit.C macro (doing a chi2 fit) is now also distributed as a tutorial
(see root.cern.ch/root/html/tutorials … Fit.C.html )

Best Regards

Lorenzo
combinedFit_LL.C (3.93 KB)

Thanks for this great example.

Currently, I am struggeling with a similar problem. However, it is a little bit more complex.

I have of the order of 100 histograms which I want to fit with the corresponding functions, i.e. for each histogram I have a special fitfunction. But as before, the functions share a parameter which should be fitted globally as well as some parameters which should only optimized for the special histogram.

I think I managed to rewrite this small program so that it is applicable to my case, however I am not sure how to change the double operator() function.
Has anyone an idea?

Thanks for the help in advance.

[quote]I am not sure how to change the double operator() function.
[/quote]

I don’t understand what you mean here. The signature of the function must say the same but you can implement as you like and you can custumize your functor class to have as data member the ones you need.

Regards

Lorenzo

Ok, how can I can I change the return statement:

    return (*fChi2_1)(p1) + (*fChi2_2)(p2);

that it works for hundred functions where only one parameter is shared and the others are different?

It is so far not clear for me.

Hey,

sorry. Of course it is pretty easy! I can just define a double variable before, let’s say foo, add in a for loop all contributions to foo and return it.

Yes, exactly, I was going to answer to you to do like this: add as data members a vector or a list for all the chi2 for each single histogram and then loop on on this term, evaluate them and add them up. When evaluating you define the parameters correctly for each one of them.
Good you found out yourself,

Cheers, Lorenzo

Hey Lorenzo,

while this fitting procedure is now working for me, I would like to ask another question just out of curiosity: how does this LL fit handel empty bins?

In the normal Fit routine of a TH1, one can specify the options “LW” so that the empty bins are included in the LL-fit.

Is something similar also here possible?

Cheers,
Stephan

Hi,

The empty bins counts in the Histogram log-likelihood fit, when you are computing the overall function normalization (basically your total number of expected events). The formula used is 33.12 of
pdg.lbl.gov/2011/reviews/rpp2011 … istics.pdf

The empty bins they are considered both if you use the fit option “L” or “LW”.
However when fitting with option “” (chi2 fit) they are not included. They are included with option “WW”, but in this case all bins will have the same weight.

Best Regards

Lorenzo

Hello,

sorry but I’m trying to adapt the script CombinedFit to my needs, and I have a really basic question.
When you type the command:

what’s the meaning of (2) after gaus?

Thank you!

It means that the first parameter of the gaussian function (i.e. the amplitude) will be par[2], i.e. the third parameter of the parameter list of the full function (gaus+exp).

See also root.cern.ch/root/html/TFormula.html

Lorenzo

Thank you Lorenzo, but does this command work also with user-defined functions? From the documentation it seems it is only used with pre-defined functions, and when I try with my script I get an error for “Bad numerical expression”!

I try to better explain my problem. I have several TGraphErrors which I would like to fit with the same function of 4 parameters. Two of these 4 parameters have to be fixed for each Graph, while two of them I would like to fit over the all set of Graphs.
My function looks like this:

Let’s make the simple case with only two Graphs to fit. According to the macro combinedFit.C I wrote the following lines:

[code]
struct GlobalChi2 {
GlobalChi2( ROOT::Math::IMultiGenFunction & f1, ROOT::Math::IMultiGenFunction & f2) :
fChi2_1(f1), fChi2_2(f2) {}

// parameter vector is first background (in common 1 and 2) and then is signal (only in 2)
double operator() (const double *par) const {
double p1[4];
p1[0] = par[0];
p1[1] = par[1];
p1[2] = par[4];
p1[3] = par[5];

  double p2[4]; 
  p2[0] = par[2]; 
  p2[1] = par[3]; 
  p2[2] = par[4]; 
  p2[3] = par[5]; 

  return fChi2_1(p1) + fChi2_2(p2);

}

const ROOT::Math::IMultiGenFunction & fChi2_1;
const ROOT::Math::IMultiGenFunction & fChi2_2;
};[/code]

…variable declarations and so on…

[code] TF1 *f1 = new TF1(“f1”,"([0]*exp(-x/[4]) + [1]*exp(-x/[5]))",0.,360.);
TF1 *f2 = new TF1(“f2”,"([2]*exp(-x/[4]) + [3]*exp(-x/[5]))",0.,360.);

ROOT::Math::WrappedMultiTF1 wf1(*f1,1);
ROOT::Math::WrappedMultiTF1 wf2(*f2,1);

ROOT::Fit::BinData dataone; 
ROOT::Fit::FillData(dataone, expH_62);

ROOT::Fit::BinData datatwo; 
ROOT::Fit::FillData(datatwo, expH_13);

ROOT::Fit::Chi2Function chi2_1(dataone, wf1);
ROOT::Fit::Chi2Function chi2_2(datatwo, wf2);

GlobalChi2 globalChi2(chi2_1, chi2_2);

ROOT::Fit::Fitter fitter;

const int Npar = 6; 
double par0[Npar] = {20., 80., 60., 40., 15., 150.};

fitter.Config().SetParamsSettings(6, par0);
fitter.Config().ParSettings(0).Fix();
fitter.Config().ParSettings(1).Fix();
fitter.Config().ParSettings(2).Fix();
fitter.Config().ParSettings(3).Fix();


fitter.FitFCN(6,globalChi2,par0,dataone.Size()+datatwo.Size());
ROOT::Fit::FitResult result = fitter.Result();
result.Print(std::cout);[/code]

Is there anything clearly wrong?
And is it a problem the following message I get when running the macro: “Limitation: Reference member not accessible from the interpreter” ?

Thank you again

Baloo