// @(#)root/roostats:$Id: cranmer $
// Author: Kyle Cranmer, George Lewis
/*************************************************************************
* Copyright (C) 1995-2008, Rene Brun and Fons Rademakers. *
* All rights reserved. *
* *
* For the licensing terms see $ROOTSYS/LICENSE. *
* For the list of contributors see $ROOTSYS/README/CREDITS. *
*************************************************************************/
////////////////////////////////////////////////////////////////////////////////
/** \class ParamHistFunc
* \ingroup HistFactory
* A class which maps the current values of a RooRealVar
* (or a set of RooRealVars) to one of a number of RooRealVars:
*
* `ParamHistFunc: {val1, val2, ...} -> {gamma (RooRealVar)}`
*
* The intended interpretation is that each parameter in the
* range represent the height of a bin over the domain
* space.
*
* The 'createParamSet' is an easy way to create these
* parameters from a set of observables. They are
* stored using the "TH1" ordering convention (as compared
* to the RooDataHist convention, which is used internally
* and one must map between the two).
*
* All indices include '0':
* \f$ \gamma_{i,j} \f$ = `paramSet[ size(i)*j + i ]`
*
* ie assuming the dimensions are 5*5:
* \f$ \gamma_{2,1} \f$ = `paramSet[ 5*1 + 2 ] = paramSet[7]`
*/
#include
#include
#include
#include "TMath.h"
#include "TH1.h"
#include "Riostream.h"
#include "RooFit.h"
#include "RooStats/HistFactory/ParamHistFunc.h"
#include "RooAbsReal.h"
#include "RooAbsPdf.h"
#include "RooConstVar.h"
#include "RooBinning.h"
#include "RooErrorHandler.h"
#include "RooGaussian.h"
#include "RooHistFunc.h"
#include "RooArgSet.h"
#include "RooNLLVar.h"
#include "RooChi2Var.h"
#include "RooMsgService.h"
// Forward declared:
#include "RooRealVar.h"
#include "RooArgList.h"
#include "RooWorkspace.h"
//using namespace std;
ClassImp(ParamHistFunc);
////////////////////////////////////////////////////////////////////////////////
ParamHistFunc::ParamHistFunc() : _numBins(0)
{
_dataSet.removeSelfFromDir(); // files must not delete _dataSet.
}
////////////////////////////////////////////////////////////////////////////////
/// Create a function which returns binewise-values
/// This class contains N RooRealVar's, one for each
/// bin from the given RooRealVar.
///
/// The value of the function in the ith bin is
/// given by:
///
/// F(i) = gamma_i * nominal(i)
///
/// Where the nominal values are simply fixed
/// numbers (default = 1.0 for all i)
ParamHistFunc::ParamHistFunc(const char* name, const char* title,
const RooArgList& vars, const RooArgList& paramSet) :
RooAbsReal(name, title),
_dataVars("!dataVars","data Vars", this),
_paramSet("!paramSet","bin parameters", this),
_numBins(0),
_dataSet( (std::string(name)+"_dataSet").c_str(), "", vars)
{
// Create the dataset that stores the binning info:
// _dataSet = RooDataSet("
_dataSet.removeSelfFromDir(); // files must not delete _dataSet.
// Set the binning
// //_binning = var.getBinning().clone() ;
// Create the set of parameters
// controlling the height of each bin
// Get the number of bins
_numBins = GetNumBins( vars );
// Add the parameters (with checking)
addVarSet( vars );
addParamSet( paramSet );
}
////////////////////////////////////////////////////////////////////////////////
/// Create a function which returns bin-wise values.
/// This class allows to multiply bin contents of histograms
/// with the values of a set of RooRealVars.
///
/// The value of the function in the ith bin is
/// given by:
/// \f[
/// F(i) = \gamma_{i} * \mathrm{nominal}(i)
/// \f]
///
/// Where the nominal values are taken from the histogram,
/// and the \f$ \gamma_{i} \f$ can be set from the outside.
ParamHistFunc::ParamHistFunc(const char* name, const char* title,
const RooArgList& vars, const RooArgList& paramSet,
const TH1* Hist ) :
RooAbsReal(name, title),
// _dataVar("!dataVar","data Var", this, (RooRealVar&) var),
_dataVars("!dataVars","data Vars", this),
_paramSet("!paramSet","bin parameters", this),
_numBins(0),
_dataSet( (std::string(name)+"_dataSet").c_str(), "", vars, Hist)
{
_dataSet.removeSelfFromDir(); // files must not delete _dataSet.
// Get the number of bins
_numBins = GetNumBins( vars );
// Add the parameters (with checking)
addVarSet( vars );
addParamSet( paramSet );
}
Int_t ParamHistFunc::GetNumBins( const RooArgSet& vars ) {
// A helper method to get the number of bins
if( vars.getSize() == 0 ) return 0;
Int_t numBins = 1;
RooFIter varIter = vars.fwdIterator() ;
RooAbsArg* comp ;
while((comp = (RooAbsArg*) varIter.next())) {
if (!dynamic_cast(comp)) {
std::cout << "ParamHistFunc::GetNumBins" << vars.GetName() << ") ERROR: component "
<< comp->GetName()
<< " in vars list is not of type RooRealVar" << std::endl ;
RooErrorHandler::softAbort() ;
return -1;
}
RooRealVar* var = (RooRealVar*) comp;
Int_t varNumBins = var->numBins();
numBins *= varNumBins;
}
return numBins;
}
////////////////////////////////////////////////////////////////////////////////
ParamHistFunc::ParamHistFunc(const ParamHistFunc& other, const char* name) :
RooAbsReal(other, name),
_dataVars("!dataVars", this, other._dataVars ),
_paramSet("!paramSet", this, other._paramSet),
_numBins( other._numBins ),
_binMap( other._binMap ),
_dataSet( other._dataSet )
{
_dataSet.removeSelfFromDir(); // files must not delete _dataSet.
// Copy constructor
// Member _ownedList is intentionally not copy-constructed -- ownership is not transferred
}
////////////////////////////////////////////////////////////////////////////////
ParamHistFunc::~ParamHistFunc()
{
;
}
////////////////////////////////////////////////////////////////////////////////
/// Get the index of the gamma parameter associated
/// with the current bin.
/// This number is the "RooDataSet" style index
/// and it must be because it uses the RooDataSet method directly
/// This is intended to be fed into the getParameter(Int_t) method:
///
/// RooRealVar currentParam = getParameter( getCurrentBin() );
Int_t ParamHistFunc::getCurrentBin() const {
Int_t dataSetIndex = _dataSet.getIndex( _dataVars ); // calcTreeIndex();
return dataSetIndex;
}
////////////////////////////////////////////////////////////////////////////////
/// Get the parameter associate with the the
/// input RooDataHist style index
/// It uses the binMap to convert the RooDataSet style index
/// into the TH1 style index (which is how they are stored
/// internally in the '_paramSet' vector
RooRealVar& ParamHistFunc::getParameter( Int_t index ) const {
Int_t gammaIndex = -1;
if( _binMap.find( index ) != _binMap.end() ) {
gammaIndex = _binMap[ index ];
}
else {
std::cout << "Error: ParamHistFunc internal bin index map "
<< "not properly configured" << std::endl;
throw -1;
}
return (RooRealVar&) _paramSet[gammaIndex];
}
////////////////////////////////////////////////////////////////////////////////
RooRealVar& ParamHistFunc::getParameter() const {
Int_t index = getCurrentBin();
return getParameter( index );
}
void ParamHistFunc::setParamConst( Int_t index, Bool_t varConst ) {
RooRealVar& var = getParameter( index );
var.setConstant( varConst );
}
void ParamHistFunc::setConstant( bool constant ) {
for( int i=0; i < numBins(); ++i) {
setParamConst(i, constant);
}
}
////////////////////////////////////////////////////////////////////////////////
void ParamHistFunc::setShape( TH1* shape ) {
int num_hist_bins = shape->GetNbinsX()*shape->GetNbinsY()*shape->GetNbinsZ();
if( num_hist_bins != numBins() ) {
std::cout << "Error - ParamHistFunc: cannot set Shape of ParamHistFunc: " << GetName()
<< " using histogram: " << shape->GetName()
<< ". Bins don't match" << std::endl;
throw std::runtime_error("setShape");
}
Int_t TH1BinNumber = 0;
for( Int_t i = 0; i < numBins(); ++i) {
TH1BinNumber++;
while( shape->IsBinUnderflow(TH1BinNumber) || shape->IsBinOverflow(TH1BinNumber) ){
TH1BinNumber++;
}
//RooRealVar& var = dynamic_cast(getParameter(i));
RooRealVar& var = dynamic_cast(_paramSet[i]);
var.setVal( shape->GetBinContent(TH1BinNumber) );
}
}
////////////////////////////////////////////////////////////////////////////////
/// Create the list of RooRealVar
/// parameters which represent the
/// height of the histogram bins.
/// The list 'vars' represents the
/// observables (corresponding to histogram bins)
/// that these newly created parameters will
/// be mapped to. (ie, we create one parameter
/// per observable in vars and per bin in each observable)
/// Store them in a list using:
/// _paramSet.add( createParamSet() );
/// This list is stored in the "TH1" index order
RooArgList ParamHistFunc::createParamSet(RooWorkspace& w, const std::string& Prefix,
const RooArgList& vars) {
// Get the number of bins
// in the nominal histogram
RooArgList paramSet;
Int_t numVars = vars.getSize();
Int_t numBins = GetNumBins( vars );
if( numVars == 0 ) {
std::cout << "Warning - ParamHistFunc::createParamSet() :"
<< " No Variables provided. Not making constraint terms."
<< std::endl;
return paramSet;
}
else if( numVars == 1 ) {
// For each bin, create a RooRealVar
for( Int_t i = 0; i < numBins; ++i) {
std::stringstream VarNameStream;
VarNameStream << Prefix << "_bin_" << i;
std::string VarName = VarNameStream.str();
RooRealVar gamma( VarName.c_str(), VarName.c_str(), 1.0 );
// "Hard-Code" a minimum of 0.0
gamma.setMin( 0.0 );
gamma.setConstant( false );
w.import( gamma, RooFit::RecycleConflictNodes() );
RooRealVar* gamma_wspace = (RooRealVar*) w.var( VarName.c_str() );
paramSet.add( *gamma_wspace );
}
}
else if( numVars == 2 ) {
// Create a vector of indices
// all starting at 0
std::vector< Int_t > Indices(numVars, 0);
RooRealVar* varx = (RooRealVar*) vars.at(0);
RooRealVar* vary = (RooRealVar*) vars.at(1);
// For each bin, create a RooRealVar
for( Int_t j = 0; j < vary->numBins(); ++j) {
for( Int_t i = 0; i < varx->numBins(); ++i) {
// Ordering is important:
// To match TH1, list goes over x bins
// first, then y
std::stringstream VarNameStream;
VarNameStream << Prefix << "_bin_" << i << "_" << j;
std::string VarName = VarNameStream.str();
RooRealVar gamma( VarName.c_str(), VarName.c_str(), 1.0 );
// "Hard-Code" a minimum of 0.0
gamma.setMin( 0.0 );
gamma.setConstant( false );
w.import( gamma, RooFit::RecycleConflictNodes() );
RooRealVar* gamma_wspace = (RooRealVar*) w.var( VarName.c_str() );
paramSet.add( *gamma_wspace );
}
}
}
else if( numVars == 3 ) {
// Create a vector of indices
// all starting at 0
std::vector< Int_t > Indices(numVars, 0);
RooRealVar* varx = (RooRealVar*) vars.at(0);
RooRealVar* vary = (RooRealVar*) vars.at(1);
RooRealVar* varz = (RooRealVar*) vars.at(2);
// For each bin, create a RooRealVar
for( Int_t k = 0; k < varz->numBins(); ++k) {
for( Int_t j = 0; j < vary->numBins(); ++j) {
for( Int_t i = 0; i < varx->numBins(); ++i) {
// Ordering is important:
// To match TH1, list goes over x bins
// first, then y, then z
std::stringstream VarNameStream;
VarNameStream << Prefix << "_bin_" << i << "_" << j << "_" << k;
std::string VarName = VarNameStream.str();
RooRealVar gamma( VarName.c_str(), VarName.c_str(), 1.0 );
// "Hard-Code" a minimum of 0.0
gamma.setMin( 0.0 );
gamma.setConstant( false );
w.import( gamma, RooFit::RecycleConflictNodes() );
RooRealVar* gamma_wspace = (RooRealVar*) w.var( VarName.c_str() );
paramSet.add( *gamma_wspace );
}
}
}
}
else {
std::cout << " Error: ParamHistFunc doesn't support dimensions > 3D " << std::endl;
}
return paramSet;
}
////////////////////////////////////////////////////////////////////////////////
/// Create the list of RooRealVar parameters which scale the
/// height of histogram bins.
/// The list `vars` represents the observables (corresponding to histogram bins)
/// that these newly created parameters will
/// be mapped to. *I.e.*, we create one parameter
/// per observable in `vars` and per bin in each observable.
///
/// The new parameters are initialised to 1 with an uncertainty of +/- 1.,
/// their range is set to the function arguments.
///
/// Store the parameters in a list using:
/// ```
/// _paramSet.add( createParamSet() );
/// ```
/// This list is stored in the "TH1" index order.
RooArgList ParamHistFunc::createParamSet(RooWorkspace& w, const std::string& Prefix,
const RooArgList& vars,
Double_t gamma_min, Double_t gamma_max) {
RooArgList params = ParamHistFunc::createParamSet( w, Prefix, vars );
for (auto comp : params) {
auto var = static_cast(comp);
var->setMin( gamma_min );
var->setMax( gamma_max );
}
return params;
}
////////////////////////////////////////////////////////////////////////////////
/// Create the list of RooRealVar
/// parameters which represent the
/// height of the histogram bins.
/// Store them in a list
RooArgList ParamHistFunc::createParamSet(const std::string& Prefix, Int_t numBins,
Double_t gamma_min, Double_t gamma_max) {
// _paramSet.add( createParamSet() );
// Get the number of bins
// in the nominal histogram
RooArgList paramSet;
if( gamma_max <= gamma_min ) {
std::cout << "Warning: gamma_min <= gamma_max: Using default values (0, 10)" << std::endl;
gamma_min = 0.0;
gamma_max = 10.0;
}
Double_t gamma_nominal = 1.0;
if( gamma_nominal < gamma_min ) {
gamma_nominal = gamma_min;
}
if( gamma_nominal > gamma_max ) {
gamma_nominal = gamma_max;
}
// For each bin, create a RooRealVar
for( Int_t i = 0; i < numBins; ++i) {
std::stringstream VarNameStream;
VarNameStream << Prefix << "_bin_" << i;
std::string VarName = VarNameStream.str();
RooRealVar* gamma = new RooRealVar( VarName.c_str(), VarName.c_str(),
gamma_nominal, gamma_min, gamma_max );
gamma->setConstant( false );
paramSet.add( *gamma );
}
return paramSet;
}
////////////////////////////////////////////////////////////////////////////////
/// return 0 for success
/// return 1 for failure
/// Check that the elements
/// are actually RooRealVar's
/// If so, add them to the
/// list of vars
Int_t ParamHistFunc::addVarSet( const RooArgList& vars ) {
int numVars = 0;
RooFIter varIter = vars.fwdIterator() ;
RooAbsArg* comp ;
while((comp = (RooAbsArg*) varIter.next())) {
if (!dynamic_cast(comp)) {
coutE(InputArguments) << "ParamHistFunc::(" << GetName() << ") ERROR: component "
<< comp->GetName() << " in variables list is not of type RooRealVar"
<< std::endl;
RooErrorHandler::softAbort() ;
return 1;
}
_dataVars.add( *comp );
numVars++;
}
Int_t numBinsX = 1;
Int_t numBinsY = 1;
Int_t numBinsZ = 1;
if( numVars == 1 ) {
RooRealVar* varX = (RooRealVar*) _dataVars.at(0);
numBinsX = varX->numBins();
numBinsY = 1;
numBinsZ = 1;
} else if( numVars == 2 ) {
RooRealVar* varX = (RooRealVar*) _dataVars.at(0);
RooRealVar* varY = (RooRealVar*) _dataVars.at(1);
numBinsX = varX->numBins();
numBinsY = varY->numBins();
numBinsZ = 1;
} else if( numVars == 3 ) {
RooRealVar* varX = (RooRealVar*) _dataVars.at(0);
RooRealVar* varY = (RooRealVar*) _dataVars.at(1);
RooRealVar* varZ = (RooRealVar*) _dataVars.at(2);
numBinsX = varX->numBins();
numBinsY = varY->numBins();
numBinsZ = varZ->numBins();
} else {
std::cout << "ParamHistFunc() - Only works for 1-3 variables (1d-3d)" << std::endl;
throw -1;
}
// Fill the mapping between
// RooDataHist bins and TH1 Bins:
// Clear the map
_binMap.clear();
// Fill the map
for( Int_t i = 0; i < numBinsX; ++i ) {
for( Int_t j = 0; j < numBinsY; ++j ) {
for( Int_t k = 0; k < numBinsZ; ++k ) {
Int_t RooDataSetBin = k + j*numBinsZ + i*numBinsY*numBinsZ;
Int_t TH1HistBin = i + j*numBinsX + k*numBinsX*numBinsY;
_binMap[RooDataSetBin] = TH1HistBin;
}
}
}
return 0;
}
////////////////////////////////////////////////////////////////////////////////
Int_t ParamHistFunc::addParamSet( const RooArgList& params ) {
// return 0 for success
// return 1 for failure
// Check that the supplied list has
// the right number of arguments:
Int_t numVarBins = _numBins;
Int_t numElements = params.getSize();
if( numVarBins != numElements ) {
std::cout << "ParamHistFunc::addParamSet - ERROR - "
<< "Supplied list of parameters " << params.GetName()
<< " has " << numElements << " elements but the ParamHistFunc"
<< GetName() << " has " << numVarBins << " bins."
<< std::endl;
return 1;
}
// Check that the elements
// are actually RooRealVar's
// If so, add them to the
// list of params
RooFIter paramIter = params.fwdIterator() ;
RooAbsArg* comp ;
while((comp = (RooAbsArg*) paramIter.next())) {
if (!dynamic_cast(comp)) {
coutE(InputArguments) << "ParamHistFunc::(" << GetName() << ") ERROR: component "
<< comp->GetName() << " in paramater list is not of type RooRealVar"
<< std::endl;
RooErrorHandler::softAbort() ;
return 1;
}
_paramSet.add( *comp );
}
return 0;
}
////////////////////////////////////////////////////////////////////////////////
Double_t ParamHistFunc::evaluate() const
{
// Find the bin cooresponding to the current
// value of the RooRealVar:
RooRealVar* param = (RooRealVar*) &(getParameter());
Double_t value = param->getVal();
return value;
}
////////////////////////////////////////////////////////////////////////////////
/// Advertise that all integrals can be handled internally.
Int_t ParamHistFunc::getAnalyticalIntegralWN(RooArgSet& allVars, RooArgSet& analVars,
const RooArgSet* normSet,
const char* /*rangeName*/) const
{
// Handle trivial no-integration scenario
if (allVars.getSize()==0) return 0 ;
if (_forceNumInt) return 0 ;
// Select subset of allVars that are actual dependents
analVars.add(allVars) ;
// Check if this configuration was created before
Int_t sterileIdx(-1) ;
CacheElem* cache = (CacheElem*) _normIntMgr.getObj(normSet,&analVars,&sterileIdx,(const char*)0) ;
if (cache) {
return _normIntMgr.lastIndex()+1 ;
}
// Create new cache element
cache = new CacheElem ;
// Store cache element
Int_t code = _normIntMgr.setObj(normSet,&analVars,(RooAbsCacheElement*)cache,0) ;
return code+1 ;
}
////////////////////////////////////////////////////////////////////////////////
/// Implement analytical integrations by doing appropriate weighting from component integrals
/// functions to integrators of components
Double_t ParamHistFunc::analyticalIntegralWN(Int_t /*code*/, const RooArgSet* /*normSet2*/,
const char* /*rangeName*/) const
{
Double_t value(0) ;
// Simply loop over bins,
// get the height, and
// multiply by the bind width
RooFIter paramIter = _paramSet.fwdIterator();
RooRealVar* param = NULL;
Int_t nominalItr = 0;
while((param = (RooRealVar*) paramIter.next())) {
// Get the gamma's value
Double_t paramVal = (*param).getVal();
// Get the bin volume
_dataSet.get( nominalItr );
Double_t binVolumeDS = _dataSet.binVolume(); //_binning->binWidth( nominalItr );
// Finally, get the subtotal
value += paramVal*binVolumeDS;
++nominalItr;
/*
std::cout << "Integrating : "
<< " bin: " << nomValue
<< " binVolume: " << binVolumeDS
<< " paramValue: " << paramVal
<< " nomValue: " << nomValue
<< " subTotal: " << value
<< std::endl;
*/
}
return value;
}
////////////////////////////////////////////////////////////////////////////////
/// Return sampling hint for making curves of (projections) of this function
/// as the recursive division strategy of RooCurve cannot deal efficiently
/// with the vertical lines that occur in a non-interpolated histogram
std::list* ParamHistFunc::plotSamplingHint(RooAbsRealLValue& obs, Double_t xlo,
Double_t xhi) const
{
// copied and edited from RooHistFunc
RooAbsLValue* lvarg = &obs;
// Retrieve position of all bin boundaries
const RooAbsBinning* binning = lvarg->getBinningPtr(0) ;
Double_t* boundaries = binning->array() ;
std::list* hint = new std::list ;
// Widen range slighty
xlo = xlo - 0.01*(xhi-xlo) ;
xhi = xhi + 0.01*(xhi-xlo) ;
Double_t delta = (xhi-xlo)*1e-8 ;
// Construct array with pairs of points positioned epsilon to the left and
// right of the bin boundaries
for (Int_t i=0 ; inumBoundaries() ; i++) {
if (boundaries[i]>=xlo && boundaries[i]<=xhi) {
hint->push_back(boundaries[i]-delta) ;
hint->push_back(boundaries[i]+delta) ;
}
}
return hint ;
}
////////////////////////////////////////////////////////////////////////////////
/// Return sampling hint for making curves of (projections) of this function
/// as the recursive division strategy of RooCurve cannot deal efficiently
/// with the vertical lines that occur in a non-interpolated histogram
std::list* ParamHistFunc::binBoundaries(RooAbsRealLValue& obs, Double_t xlo,
Double_t xhi) const
{
// copied and edited from RooHistFunc
RooAbsLValue* lvarg = &obs;
// Retrieve position of all bin boundaries
const RooAbsBinning* binning = lvarg->getBinningPtr(0) ;
Double_t* boundaries = binning->array() ;
std::list* hint = new std::list ;
// Construct array with pairs of points positioned epsilon to the left and
// right of the bin boundaries
for (Int_t i=0 ; inumBoundaries() ; i++) {
if (boundaries[i]>=xlo && boundaries[i]<=xhi) {
hint->push_back(boundaries[i]) ;
}
}
return hint ;
}