Serialization of TFitResult

I attempted yesterday to save a TFitResult to a ROOT file, then read it back and get the error on a point using TFitResult::GetConfidenceIntervals(). This does not provide the correct result; it seems to put an uninitialized value in the error array. I have attached a minimum example to demonstrate the problem, with the following output (before serialization vs. after serialization):

f(x=1) = 69.0872 +/- 2.9724
f(x=1) = 69.0872 +/- 0

testfitresult.C (925 Bytes)

I tested this in several different ROOT versions, listed below.


ROOT Version: 6.06.00
Platform: SLC6
Compiler: gcc530


ROOT Version: 6.12.07
Platform: SLC6
Compiler: gcc700


UPDATE: after the discussion below, I read through the code more carefully and developed a simple extension of the TFitResult class that exposes the SetModelFunction() accessor publicly. Using this extended class to wrap a serialized TFitResult object restores the correct behavior, as long as the appropriate function is provided. My implementation accepts a TF1 as the provided function and does all the necessary conversions. The code for the extended class itself is provided here, as well as a working macro attached:

testfitresult2.C (1.3 KB)

class KFitResult : public TFitResult {
	public:
	using TFitResult::TFitResult;
	void ResetModelFunction(TF1* func){
		this->SetModelFunction(std::shared_ptr<IModelFunction>(dynamic_cast<IModelFunction*>(ROOT::Math::WrappedMultiTF1(*func).Clone())));
	}
};

Write() is implemented in TObject, and GetConfidenceIntervals() and fFitFunc is actually saved in ROOT::Fit::FitResult, the Write() will not able to save information from a class not derived from TObject. And the function to get confidence level rely on info from ROOT::Fit::FitResult. I actually see the error “Error in <ROOT::Math::FitResult::GetConfidenceIntervals>: Cannot compute Confidence Intervals without fit model function”. Maybe you have pipe your stderr or suppressed the msg so you didn’t see this.
image

A small-scale practical solution is to use TTree to save information you need, and then simply hadd for serialization. Otherwise you can check RooWorkspace and ModelConfig for better saving of your likelihood and model definition.

That makes sense. The TTree solution seems like a feasible, if somewhat clunky, workaround.

The ROOT team might want to adjust the TH1 documentation. In the description of the TH1::Fit() function (https://root.cern.ch/doc/master/classTH1.html#a7e7d34c91d5ebab4fc9bba3ca47dabdd), in the section " Access to the fit result", it suggests using this pattern:

TFitResultPtr r = h->Fit(myFunc,"S");
...
r->Write();        // store the result in a file

But the full fit result cannot actually be stored, so this is misleading.

the Write() will not able to save information from a class not derived from TObject.

Actually it does, the function TObject::Write can detect the actual (derived) type and use the class hierarchy information we have (TClass and friends) to call the proper streaming operation.

The challenge is that FitResult::GetConfidenceIntervals requires the data member fFitFunc to be valid:

   if (!fFitFunc) {
      // check if model function exists
      MATH_ERROR_MSG("FitResult::GetConfidenceIntervals","Cannot compute Confidence Intervals without fit model function");
      return;
   }

However the member is marked as transient:

   std::shared_ptr<IModelFunction> fFitFunc; //! model function resulting  from the fit. 

(besides that we do not support yet shared_ptr), this is because in the general sense the function can not be saved as it is often pointing/using to a C++ function pointer (i.e. the I/O has no way of finding it again).

In this use case, (using a TFormula), the function can (and has) be saved but the connection needs to be made again by hand … but since FitResult::SetModelFunction is protected this is not easy to do (i.e. requires hack).
@moneta Do you have any idea?

Cheers,
Philippe.

1 Like

Hi,

As Philippe mentioned the problem is in storing the function, which can be done only in TFormula.
Now, if you need to compute afterwards the ConfidenceIntervals, then this is problematic.
The reason FitResult::SetModelFunction is protected is that you cannot set whatever function in TFitResult, but it makes sense only for the function used for the fit.

I think the simplest solution is to have saved the data and also the function definition, so you can re-create the function but re-do the fit afterwards using as initial values the stored parameters in TFitResult.

I think an error message should have been printed when using FitResult::GetConfidenceIntervals without a function

Lorenzo

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.