Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions README/ReleaseNotes/v642/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,19 @@ the cut instead of being selected based on `sqrt(abs(x))`.

## RooFit

### Exact bin-integrated residual and pull histograms

The new `RooFit::makeResidHist()` and `RooFit::makePullHist()` functions create residual and pull histograms of binned data with respect to a fitted model, where the model expectation per bin is computed by integrating the model exactly over the bin.
The existing `RooPlot::residHist()` and `RooPlot::pullHist()` instead interpolate or average the curve of the plotted pdf, which biases the comparison for strongly peaked pdfs: it produces a characteristic "wiggle" pattern in the residuals and unrealistically large chi-square values when the binned data is compared with the result of an unbinned fit.
The new functions take the model (any `RooAbsReal`) and the data (`RooAbsData`, which is binned internally if unbinned) and support the usual RooFit command arguments, such as `RooFit::Binning()` to select the binning (also for unbinned data), `RooFit::Range()` to restrict the comparison to (a union of) fit ranges and normalize the model accordingly, `RooFit::Normalization()` for an extra scale factor, and `RooFit::DataError()` to select the point error model.

```cpp
RooDataHist binData("binData", "binData", x, *unbinnedData);
auto pull = RooFit::makePullHist(pdf, binData);
```

For comparisons against projected pdfs or components, project the model with `RooAbsPdf::createProjection()` and the data with `RooAbsData::reduce()`, or pass the component pdf with a matching `RooFit::Normalization()` argument.

### RooFit::MultiProcess without ZeroMQ, now enabled by default

The `RooFit::MultiProcess` package that implements the parallel gradient minimization with `fitTo(..., RooFit::Parallelize(n))` previously communicated between the forked processes with ZeroMQ sockets, which required building ROOT with `roofit_multiprocess=ON` and the ZeroMQ (with draft API) and cppzmq dependencies.
Expand Down
83 changes: 83 additions & 0 deletions roofit/roofitcore/inc/RooGlobalFunc.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <TColor.h>

#include <map>
#include <memory>
#include <string>

class RooDataHist ;
Expand All @@ -44,6 +45,7 @@ class RooConstVar ;
class RooRealVar ;
class RooAbsCategory ;
class RooNumIntConfig ;
class RooHist;

class TH1 ;
class TTree ;
Expand Down Expand Up @@ -472,6 +474,87 @@ RooConstVar& RooConst(double val) ;
* @}
*/

/**
* \defgroup Residuals Residuals and pulls of binned data vs. a fitted model
*
* RooFit::makeResidHist() and RooFit::makePullHist() construct per-bin
* residual or pull distributions of binned data with respect to a fitted
* model. In contrast to RooPlot::residHist() and RooPlot::pullHist(),
* which interpolate or average the curve of a plotted pdf, these functions
* integrate the model itself exactly over each bin. This avoids the
* biased residuals (the "residual wiggle" and inflated \f$\chi^2\f$ values)
* that appear when a sharply peaked pdf is compared with binned data.
* @{
*/

/// Creates a RooHist with the per-bin residuals `data` - `fitModel`.
///
/// For each bin, the model expectation is the model integrated exactly over
/// the bin (with RooAbsReal::createIntegral()), normalized to the weight of
/// the data inside the normalization range. Unbinned input data is binned
Comment on lines +493 to +494

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That it's a weight is an implementation detail. Most users probably think of data as counts.

Suggested change
/// the bin (with RooAbsReal::createIntegral()), normalized to the weight of
/// the data inside the normalization range. Unbinned input data is binned
/// the bin (with RooAbsReal::createIntegral()), normalized to
/// the data inside the normalization range. Unbinned input data is binned

/// internally. The supported RooCmdArgs are:
/// - `Binning(binning)` / `Binning(nbins)` / `Binning(nbins, xlo, xhi)` :
/// the binning used to bin unbinned input data (default: the current
/// binning of the observable)
/// - `Range(lo, hi)` / `Range("name")` : the range that the model
/// expectation is normalized in. Only bins inside this range get points
Comment on lines +499 to +500

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// - `Range(lo, hi)` / `Range("name")` : the range that the model
/// expectation is normalized in. Only bins inside this range get points
/// - `Range(lo, hi)` / `Range("name")` : compute residuals only in this range.
/// The model expectation is normalized in this range.

/// in the result. Range("name") can be passed multiple times for a union
/// of named ranges (sidebands). Use it for sideband fits or blinded data,
/// so that the model is normalized in the same range as the fitted data.
/// - `Normalization(double)` : additional scale factor for the expectation
/// (default 1.0). The scale types of Normalization(scale, type) are not
/// supported.
/// - `DataError(RooAbsData::ErrorType)` : error model used for the point errors,
/// one of Poisson, SumW2 or Auto
/// (default: Auto, meaning SumW2 for weighted and Poisson for unweighted data)
/// - `Name(const char*)`, `Title(const char*)` : name and title of the created RooHist
///
/// \note The residuals are computed with respect to `fitModel` itself, not
/// with respect to the curve of a plotted pdf. Make sure that the arguments
/// are consistent with what is plotted (same normalization and range, same
/// error model via DataError()), or the residual/pull histogram will not
/// match the displayed data and curve.
///
/// \note If the model depends on observables beyond the data's observable
/// (e.g. a conditional pdf), they are evaluated at their current values.
/// There is no exact equivalent for a projection with ProjWData().
///
/// \note For internal use of RooAbsReal::createIntegral(), a scratch named
/// range is left on the model's observable after the call.
Comment on lines +522 to +523

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

scratch named range?

A range with the name "xxx" is declared?

///
/// Only 1-dimensional data is supported, otherwise std::invalid_argument is
/// thrown; the same exception is thrown for empty input data, unknown named
/// ranges or binnings, or when the model has no support in the normalization
/// range. For higher-dimensional fits, project both the data and the
/// model onto the observable of interest:
/// ~~~{.cpp}
/// std::unique_ptr<RooAbsData> projData{data.reduce(RooArgSet{x})};
/// std::unique_ptr<RooAbsPdf> projModel{model.createProjection(RooArgSet{y})};
/// auto pulls = RooFit::makePullHist(*projModel, *projData);
/// ~~~
/// For a residual/pull histogram against one component of a composite pdf,
/// pass the component pdf itself, scaled by its coefficient:
/// ~~~{.cpp}
/// auto& background = static_cast<RooAbsPdf&>(*addPdf.pdfList().find("bkg"));
/// auto pulls = RooFit::makePullHist(background, data,
/// RooFit::Normalization(bkgFraction.getVal()));
/// ~~~
std::unique_ptr<RooHist>
makeResidHist(RooAbsReal &fitModel, RooAbsData const &data, RooCmdArg const &arg1 = {}, RooCmdArg const &arg2 = {},

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that these are new functions, would it make sense to declare the cmdArgs as a variadic template, create a vector or list of these, and pass those on to internal funcs?

RooCmdArg const &arg3 = {}, RooCmdArg const &arg4 = {}, RooCmdArg const &arg5 = {},
RooCmdArg const &arg6 = {}, RooCmdArg const &arg7 = {}, RooCmdArg const &arg8 = {});

/// Like makeResidHist(), but the residuals are divided by the corresponding
/// data uncertainty, creating a pull distribution.
std::unique_ptr<RooHist>
makePullHist(RooAbsReal &fitModel, RooAbsData const &data, RooCmdArg const &arg1 = {}, RooCmdArg const &arg2 = {},
RooCmdArg const &arg3 = {}, RooCmdArg const &arg4 = {}, RooCmdArg const &arg5 = {},
RooCmdArg const &arg6 = {}, RooCmdArg const &arg7 = {}, RooCmdArg const &arg8 = {});

/**
* @}
*/

namespace Detail {

// Function to pack an arbitrary number of RooCmdArgs into a RooLinkedList. Implementation detail of many high-level RooFit functions.
Expand Down
5 changes: 5 additions & 0 deletions roofit/roofitcore/inc/RooHist.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ class RooHist : public TGraphAsymmErrors, public RooPlotable {

bool hasIdenticalBinning(const RooHist& other) const ;

/// Compute residuals with respect to a curve.
/// \note For residuals/pulls of binned data against a fitted model, the
/// more accurate RooFit::makeResidHist() and RooFit::makePullHist() are
/// the recommended interface: they integrate the model itself exactly over
/// each bin, instead of interpolating the curve.
RooHist* makeResidHist(const RooCurve& curve,bool normalize=false, bool useAverage=false) const;
RooHist* makePullHist(const RooCurve& curve, bool useAverage=false) const

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the above comment also apply to this func? Consider \copydetail if you don't want to repeat it.

{return makeResidHist(curve,true,useAverage); }
Expand Down
8 changes: 7 additions & 1 deletion roofit/roofitcore/inc/RooPlot.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,14 @@ class RooPlot : public TNamed, public RooPrintable {
double chiSquare(int nFitParam=0) const { return chiSquare(nullptr,nullptr,nFitParam) ; }
double chiSquare(const char* pdfname, const char* histname, int nFitParam=0) const ;

// NOTE for developers: residHist() and pullHist() are superseded by
// RooFit::makeResidHist() / RooFit::makePullHist(). They should be
// deprecated in ROOT v6.44 and removed in v6.46.
RooHist* residHist(const char* histname=nullptr, const char* pdfname=nullptr,bool normalize=false, bool useAverage=true) const ;
///Uses residHist() and sets normalize=true
///Uses residHist() and sets normalize=true.
///\note For pulls of binned data against a fitted model, the more accurate
///RooFit::makePullHist() is the recommended interface: it integrates the
///model itself exactly over each bin, instead of interpolating the curve.
RooHist* pullHist(const char* histname=nullptr, const char* pdfname=nullptr, bool useAverage=true) const
{ return residHist(histname,pdfname,true,useAverage); }

Expand Down
Loading
Loading