Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
RooHistFunc.cxx
Go to the documentation of this file.
1 /*****************************************************************************
2  * Project: RooFit *
3  * Package: RooFitCore *
4  * @(#)root/roofit:$Id$
5  * Authors: *
6  * WV, Wouter Verkerke, UC Santa Barbara, verkerke@slac.stanford.edu *
7  * DK, David Kirkby, UC Irvine, dkirkby@uci.edu *
8  * *
9  * Copyright (c) 2000-2005, Regents of the University of California *
10  * and Stanford University. All rights reserved. *
11  * *
12  * Redistribution and use in source and binary forms, *
13  * with or without modification, are permitted according to the terms *
14  * listed in LICENSE (http://roofit.sourceforge.net/license.txt) *
15  *****************************************************************************/
16 
17 /**
18 \file RooHistFunc.cxx
19 \class RooHistFunc
20 \ingroup Roofitcore
21 
22 RooHistFunc implements a real-valued function sampled from a
23 multidimensional histogram. The histogram can have an arbitrary number of real or
24 discrete dimensions and may have negative values.
25 **/
26 
27 #include "RooFit.h"
28 #include "Riostream.h"
29 
30 #include "RooHistFunc.h"
31 #include "RooDataHist.h"
32 #include "RooMsgService.h"
33 #include "RooRealVar.h"
34 #include "RooCategory.h"
35 #include "RooWorkspace.h"
36 
37 #include "TError.h"
38 
39 using namespace std;
40 
41 ClassImp(RooHistFunc);
42 ;
43 
44 
45 
46 ////////////////////////////////////////////////////////////////////////////////
47 /// Default constructor
48 
49 RooHistFunc::RooHistFunc() :
50  _dataHist(0),
51  _intOrder(0),
52  _cdfBoundaries(kFALSE),
53  _totVolume(0),
54  _unitNorm(kFALSE)
55 {
56  TRACE_CREATE
57 }
58 
59 
60 ////////////////////////////////////////////////////////////////////////////////
61 /// Constructor from a RooDataHist. The variable listed in 'vars' control the dimensionality of the
62 /// function. Any additional dimensions present in 'dhist' will be projected out. RooDataHist dimensions
63 /// can be either real or discrete. See RooDataHist::RooDataHist for details on the binning.
64 /// RooHistFunc neither owns or clone 'dhist' and the user must ensure the input histogram exists
65 /// for the entire life span of this function.
66 
67 RooHistFunc::RooHistFunc(const char *name, const char *title, const RooArgSet& vars,
68  const RooDataHist& dhist, Int_t intOrder) :
69  RooAbsReal(name,title),
70  _depList("depList","List of dependents",this),
71  _dataHist((RooDataHist*)&dhist),
72  _codeReg(10),
73  _intOrder(intOrder),
74  _cdfBoundaries(kFALSE),
75  _totVolume(0),
76  _unitNorm(kFALSE)
77 {
78  _histObsList.addClone(vars) ;
79  _depList.add(vars) ;
80 
81  // Verify that vars and dhist.get() have identical contents
82  const RooArgSet* dvars = dhist.get() ;
83  if (vars.getSize()!=dvars->getSize()) {
84  coutE(InputArguments) << "RooHistFunc::ctor(" << GetName()
85  << ") ERROR variable list and RooDataHist must contain the same variables." << endl ;
86  assert(0) ;
87  }
88 
89  for (const auto arg : vars) {
90  if (!dvars->find(arg->GetName())) {
91  coutE(InputArguments) << "RooHistFunc::ctor(" << GetName()
92  << ") ERROR variable list and RooDataHist must contain the same variables." << endl ;
93  assert(0) ;
94  }
95  }
96 
97  TRACE_CREATE
98 }
99 
100 
101 
102 ////////////////////////////////////////////////////////////////////////////////
103 /// Constructor from a RooDataHist. The variable listed in 'vars' control the dimensionality of the
104 /// function. Any additional dimensions present in 'dhist' will be projected out. RooDataHist dimensions
105 /// can be either real or discrete. See RooDataHist::RooDataHist for details on the binning.
106 /// RooHistFunc neither owns or clone 'dhist' and the user must ensure the input histogram exists
107 /// for the entire life span of this function.
108 
109 RooHistFunc::RooHistFunc(const char *name, const char *title, const RooArgList& funcObs, const RooArgList& histObs,
110  const RooDataHist& dhist, Int_t intOrder) :
111  RooAbsReal(name,title),
112  _depList("depList","List of dependents",this),
113  _dataHist((RooDataHist*)&dhist),
114  _codeReg(10),
115  _intOrder(intOrder),
116  _cdfBoundaries(kFALSE),
117  _totVolume(0),
118  _unitNorm(kFALSE)
119 {
120  _histObsList.addClone(histObs) ;
121  _depList.add(funcObs) ;
122 
123  // Verify that vars and dhist.get() have identical contents
124  const RooArgSet* dvars = dhist.get() ;
125  if (histObs.getSize()!=dvars->getSize()) {
126  coutE(InputArguments) << "RooHistFunc::ctor(" << GetName()
127  << ") ERROR variable list and RooDataHist must contain the same variables." << endl ;
128  assert(0) ;
129  }
130 
131  for (const auto arg : histObs) {
132  if (!dvars->find(arg->GetName())) {
133  coutE(InputArguments) << "RooHistFunc::ctor(" << GetName()
134  << ") ERROR variable list and RooDataHist must contain the same variables." << endl ;
135  assert(0) ;
136  }
137  }
138 
139  TRACE_CREATE
140 }
141 
142 
143 
144 ////////////////////////////////////////////////////////////////////////////////
145 /// Copy constructor
146 
147 RooHistFunc::RooHistFunc(const RooHistFunc& other, const char* name) :
148  RooAbsReal(other,name),
149  _depList("depList",this,other._depList),
150  _dataHist(other._dataHist),
151  _codeReg(other._codeReg),
152  _intOrder(other._intOrder),
153  _cdfBoundaries(other._cdfBoundaries),
154  _totVolume(other._totVolume),
155  _unitNorm(other._unitNorm)
156 {
157  TRACE_CREATE
158 
159  _histObsList.addClone(other._histObsList) ;
160 }
161 
162 
163 
164 ////////////////////////////////////////////////////////////////////////////////
165 
166 RooHistFunc::~RooHistFunc()
167 {
168  TRACE_DESTROY
169 }
170 
171 
172 
173 
174 ////////////////////////////////////////////////////////////////////////////////
175 /// Return the current value: The value of the bin enclosing the current coordinates
176 /// of the dependents, normalized by the histograms contents. Interpolation
177 /// is applied if the RooHistFunc is configured to do that
178 
179 Double_t RooHistFunc::evaluate() const
180 {
181  // Transfer values from
182  if (_depList.getSize()>0) {
183  for (auto i = 0u; i < _histObsList.size(); ++i) {
184  const auto harg = _histObsList[i];
185  const auto parg = _depList[i];
186 
187  if (harg != parg) {
188  parg->syncCache() ;
189  harg->copyCache(parg,kTRUE) ;
190  if (!harg->inRange(0)) {
191  return 0 ;
192  }
193  }
194  }
195  }
196 
197  Double_t ret = _dataHist->weight(_histObsList,_intOrder,kFALSE,_cdfBoundaries) ;
198  return ret ;
199 }
200 
201 ////////////////////////////////////////////////////////////////////////////////
202 /// Only handle case of maximum in all variables
203 
204 Int_t RooHistFunc::getMaxVal(const RooArgSet& vars) const
205 {
206  RooAbsCollection* common = _depList.selectCommon(vars) ;
207  if (common->getSize()==_depList.getSize()) {
208  delete common ;
209  return 1;
210  }
211  delete common ;
212  return 0 ;
213 }
214 
215 ////////////////////////////////////////////////////////////////////////////////
216 
217 Double_t RooHistFunc::maxVal(Int_t code) const
218 {
219  R__ASSERT(code==1) ;
220 
221  Double_t max(-1) ;
222  for (Int_t i=0 ; i<_dataHist->numEntries() ; i++) {
223  _dataHist->get(i) ;
224  Double_t wgt = _dataHist->weight() ;
225  if (wgt>max) max=wgt ;
226  }
227 
228  return max*1.05 ;
229 }
230 
231 ////////////////////////////////////////////////////////////////////////////////
232 /// Return the total volume spanned by the observables of the RooDataHist
233 
234 Double_t RooHistFunc::totVolume() const
235 {
236  // Return previously calculated value, if any
237  if (_totVolume>0) {
238  return _totVolume ;
239  }
240  _totVolume = 1. ;
241  for (const auto arg : _depList) {
242  RooRealVar* real = dynamic_cast<RooRealVar*>(arg) ;
243  if (real) {
244  _totVolume *= (real->getMax()-real->getMin()) ;
245  } else {
246  RooCategory* cat = dynamic_cast<RooCategory*>(arg) ;
247  if (cat) {
248  _totVolume *= cat->numTypes() ;
249  }
250  }
251  }
252 
253  return _totVolume ;
254 }
255 
256 
257 
258 ////////////////////////////////////////////////////////////////////////////////
259 /// Determine integration scenario. If no interpolation is used,
260 /// RooHistFunc can perform all integrals over its dependents
261 /// analytically via partial or complete summation of the input
262 /// histogram. If interpolation is used, only the integral
263 /// over all RooHistPdf observables is implemented.
264 
265 Int_t RooHistFunc::getAnalyticalIntegral(RooArgSet& allVars, RooArgSet& analVars, const char* rangeName) const
266 {
267 
268  // Only analytical integrals over the full range are defined
269  if (rangeName!=0) {
270  return 0 ;
271  }
272 
273  // Simplest scenario, integrate over all dependents
274  RooAbsCollection *allVarsCommon = allVars.selectCommon(_depList) ;
275  Bool_t intAllObs = (allVarsCommon->getSize()==_depList.getSize()) ;
276  delete allVarsCommon ;
277  if (intAllObs && matchArgs(allVars,analVars,_depList)) {
278  return 1000 ;
279  }
280 
281  // Disable partial analytical integrals if interpolation is used
282  if (_intOrder>0) {
283  return 0 ;
284  }
285 
286  // Find subset of _depList that integration is requested over
287  RooArgSet* allVarsSel = (RooArgSet*) allVars.selectCommon(_depList) ;
288  if (allVarsSel->getSize()==0) {
289  delete allVarsSel ;
290  return 0 ;
291  }
292 
293  // Partial integration scenarios.
294  // Build unique code from bit mask of integrated variables in depList
295  Int_t code(0),n(0) ;
296  for (const auto arg : _depList) {
297  if (allVars.find(arg->GetName())) code |= (1<<n) ;
298  n++ ;
299  }
300 
301  analVars.add(*allVarsSel) ;
302 
303  return code ;
304 
305 }
306 
307 
308 
309 ////////////////////////////////////////////////////////////////////////////////
310 /// Return integral identified by 'code'. The actual integration
311 /// is deferred to RooDataHist::sum() which implements partial
312 /// or complete summation over the histograms contents
313 
314 Double_t RooHistFunc::analyticalIntegral(Int_t code, const char* /*rangeName*/) const
315 {
316  // WVE needs adaptation for rangeName feature
317 
318  // Simplest scenario, integration over all dependents
319  if (code==1000) {
320  return _dataHist->sum(kTRUE) ;
321  }
322 
323  // Partial integration scenario, retrieve set of variables, calculate partial sum
324  RooArgSet intSet ;
325  Int_t n(0) ;
326  for (const auto arg : _depList) {
327  if (code & (1<<n)) {
328  intSet.add(*arg) ;
329  }
330  n++ ;
331  }
332 
333  if (_depList.getSize()>0) {
334  for (auto i = 0u; i < _histObsList.size(); ++i) {
335  const auto harg = _histObsList[i];
336  const auto parg = _depList[i];
337 
338  if (harg != parg) {
339  parg->syncCache() ;
340  harg->copyCache(parg,kTRUE) ;
341  if (!harg->inRange(0)) {
342  return 0 ;
343  }
344  }
345  }
346  }
347 
348  Double_t ret = _dataHist->sum(intSet,_histObsList,kTRUE) ;
349  return ret ;
350 }
351 
352 
353 
354 ////////////////////////////////////////////////////////////////////////////////
355 /// Return sampling hint for making curves of (projections) of this function
356 /// as the recursive division strategy of RooCurve cannot deal efficiently
357 /// with the vertical lines that occur in a non-interpolated histogram
358 
359 list<Double_t>* RooHistFunc::plotSamplingHint(RooAbsRealLValue& obs, Double_t xlo, Double_t xhi) const
360 {
361  // No hints are required when interpolation is used
362  if (_intOrder>1) {
363  return 0 ;
364  }
365 
366 
367  // Find histogram observable corresponding to pdf observable
368  RooAbsArg* hobs(0) ;
369  for (auto i = 0u; i < _histObsList.size(); ++i) {
370  const auto harg = _histObsList[i];
371  const auto parg = _depList[i];
372  if (string(parg->GetName())==obs.GetName()) {
373  hobs=harg ;
374  }
375  }
376  if (!hobs) {
377  return 0 ;
378  }
379 
380  // Check that observable is in dataset, if not no hint is generated
381  RooAbsLValue* lvarg = dynamic_cast<RooAbsLValue*>(_dataHist->get()->find(hobs->GetName())) ;
382  if (!lvarg) {
383  return 0 ;
384  }
385 
386  // Retrieve position of all bin boundaries
387  const RooAbsBinning* binning = lvarg->getBinningPtr(0) ;
388  Double_t* boundaries = binning->array() ;
389 
390  list<Double_t>* hint = new list<Double_t> ;
391 
392  // Widen range slighty
393  xlo = xlo - 0.01*(xhi-xlo) ;
394  xhi = xhi + 0.01*(xhi-xlo) ;
395 
396  Double_t delta = (xhi-xlo)*1e-8 ;
397 
398  // Construct array with pairs of points positioned epsilon to the left and
399  // right of the bin boundaries
400  for (Int_t i=0 ; i<binning->numBoundaries() ; i++) {
401  if (boundaries[i]>=xlo && boundaries[i]<=xhi) {
402  hint->push_back(boundaries[i]-delta) ;
403  hint->push_back(boundaries[i]+delta) ;
404  }
405  }
406 
407  return hint ;
408 }
409 
410 
411 ////////////////////////////////////////////////////////////////////////////////
412 /// Return sampling hint for making curves of (projections) of this function
413 /// as the recursive division strategy of RooCurve cannot deal efficiently
414 /// with the vertical lines that occur in a non-interpolated histogram
415 
416 std::list<Double_t>* RooHistFunc::binBoundaries(RooAbsRealLValue& obs, Double_t xlo, Double_t xhi) const
417 {
418  // No hints are required when interpolation is used
419  if (_intOrder>1) {
420  return 0 ;
421  }
422 
423  // Find histogram observable corresponding to pdf observable
424  RooAbsArg* hobs(0) ;
425  for (auto i = 0u; i < _histObsList.size(); ++i) {
426  const auto harg = _histObsList[i];
427  const auto parg = _depList[i];
428  if (string(parg->GetName())==obs.GetName()) {
429  hobs=harg ;
430  }
431  }
432 
433  // cout << "RooHistFunc::bb(" << GetName() << ") histObs = " << _histObsList << endl ;
434  // cout << "RooHistFunc::bb(" << GetName() << ") pdfObs = " << _depList << endl ;
435 
436  RooAbsRealLValue* transform(0) ;
437  if (!hobs) {
438 
439  // Considering alternate: input observable is histogram observable and pdf observable is transformation in terms of it
440  RooAbsArg* pobs(0) ;
441  for (auto i = 0u; i < _histObsList.size(); ++i) {
442  const auto harg = _histObsList[i];
443  const auto parg = _depList[i];
444  if (string(harg->GetName())==obs.GetName()) {
445  pobs=parg ;
446  hobs=harg ;
447  }
448  }
449 
450  // Not found, or check that matching pdf observable is an l-value dependent on histogram observable fails
451  if (!hobs || !(pobs->dependsOn(obs) && dynamic_cast<RooAbsRealLValue*>(pobs))) {
452  cout << "RooHistFunc::binBoundaries(" << GetName() << ") obs = " << obs.GetName() << " hobs is not found, returning null" << endl ;
453  return 0 ;
454  }
455 
456  // Now we are in business - we are in a situation where the pdf observable LV(x), mapping to a histogram observable x
457  // We can return bin boundaries by mapping the histogram boundaties through the inverse of the LV(x) transformation
458  transform = dynamic_cast<RooAbsRealLValue*>(pobs) ;
459  }
460 
461 
462  // cout << "hobs = " << hobs->GetName() << endl ;
463  // cout << "transform = " << (transform?transform->GetName():"<none>") << endl ;
464 
465  // Check that observable is in dataset, if not no hint is generated
466  RooAbsArg* xtmp = _dataHist->get()->find(hobs->GetName()) ;
467  if (!xtmp) {
468  cout << "RooHistFunc::binBoundaries(" << GetName() << ") hobs = " << hobs->GetName() << " is not found in dataset?" << endl ;
469  _dataHist->get()->Print("v") ;
470  return 0 ;
471  }
472  RooAbsLValue* lvarg = dynamic_cast<RooAbsLValue*>(_dataHist->get()->find(hobs->GetName())) ;
473  if (!lvarg) {
474  cout << "RooHistFunc::binBoundaries(" << GetName() << ") hobs = " << hobs->GetName() << " but is not an LV, returning null" << endl ;
475  return 0 ;
476  }
477 
478  // Retrieve position of all bin boundaries
479  const RooAbsBinning* binning = lvarg->getBinningPtr(0) ;
480  Double_t* boundaries = binning->array() ;
481 
482  list<Double_t>* hint = new list<Double_t> ;
483 
484  Double_t delta = (xhi-xlo)*1e-8 ;
485 
486  // Construct array with pairs of points positioned epsilon to the left and
487  // right of the bin boundaries
488  for (Int_t i=0 ; i<binning->numBoundaries() ; i++) {
489  if (boundaries[i]>xlo-delta && boundaries[i]<xhi+delta) {
490 
491  Double_t boundary = boundaries[i] ;
492  if (transform) {
493  transform->setVal(boundary) ;
494  //cout << "transform bound " << boundary << " using " << transform->GetName() << " result " << obs.getVal() << endl ;
495  hint->push_back(obs.getVal()) ;
496  } else {
497  hint->push_back(boundary) ;
498  }
499  }
500  }
501 
502  return hint ;
503 }
504 
505 
506 
507 ////////////////////////////////////////////////////////////////////////////////
508 /// Check if our datahist is already in the workspace
509 
510 Bool_t RooHistFunc::importWorkspaceHook(RooWorkspace& ws)
511 {
512  std::list<RooAbsData*> allData = ws.allEmbeddedData() ;
513  std::list<RooAbsData*>::const_iterator iter ;
514  for (iter = allData.begin() ; iter != allData.end() ; ++iter) {
515  // If your dataset is already in this workspace nothing needs to be done
516  if (*iter == _dataHist) {
517  return kFALSE ;
518  }
519  }
520 
521  // Check if dataset with given name already exists
522  RooAbsData* wsdata = ws.embeddedData(_dataHist->GetName()) ;
523 
524  if (wsdata) {
525 
526  // Yes it exists - now check if it is identical to our internal histogram
527  if (wsdata->InheritsFrom(RooDataHist::Class())) {
528 
529  // Check if histograms are identical
530  if (areIdentical((RooDataHist&)*wsdata,*_dataHist)) {
531 
532  // Exists and is of correct type, and identical -- adjust internal pointer to WS copy
533  _dataHist = (RooDataHist*) wsdata ;
534  } else {
535 
536  // not identical, clone rename and import
537  TString uniqueName = Form("%s_%s",_dataHist->GetName(),GetName()) ;
538  Bool_t flag = ws.import(*_dataHist,RooFit::Rename(uniqueName.Data()),RooFit::Embedded()) ;
539  if (flag) {
540  coutE(ObjectHandling) << " RooHistPdf::importWorkspaceHook(" << GetName() << ") unable to import clone of underlying RooDataHist with unique name " << uniqueName << ", abort" << endl ;
541  return kTRUE ;
542  }
543  _dataHist = (RooDataHist*) ws.embeddedData(uniqueName.Data()) ;
544  }
545 
546  } else {
547 
548  // Exists and is NOT of correct type: clone rename and import
549  TString uniqueName = Form("%s_%s",_dataHist->GetName(),GetName()) ;
550  Bool_t flag = ws.import(*_dataHist,RooFit::Rename(uniqueName.Data()),RooFit::Embedded()) ;
551  if (flag) {
552  coutE(ObjectHandling) << " RooHistPdf::importWorkspaceHook(" << GetName() << ") unable to import clone of underlying RooDataHist with unique name " << uniqueName << ", abort" << endl ;
553  return kTRUE ;
554  }
555  _dataHist = (RooDataHist*) ws.embeddedData(uniqueName.Data()) ;
556 
557  }
558  return kFALSE ;
559  }
560 
561  // We need to import our datahist into the workspace
562  ws.import(*_dataHist,RooFit::Embedded()) ;
563 
564  // Redirect our internal pointer to the copy in the workspace
565  _dataHist = (RooDataHist*) ws.embeddedData(_dataHist->GetName()) ;
566  return kFALSE ;
567 }
568 
569 
570 ////////////////////////////////////////////////////////////////////////////////
571 
572 Bool_t RooHistFunc::areIdentical(const RooDataHist& dh1, const RooDataHist& dh2)
573 {
574  if (fabs(dh1.sumEntries()-dh2.sumEntries())>1e-8) return kFALSE ;
575  if (dh1.numEntries() != dh2.numEntries()) return kFALSE ;
576  for (int i=0 ; i < dh1.numEntries() ; i++) {
577  dh1.get(i) ;
578  dh2.get(i) ;
579  if (fabs(dh1.weight()-dh2.weight())>1e-8) return kFALSE ;
580  }
581  if (!(RooNameSet(*dh1.get())==RooNameSet(*dh2.get()))) return kFALSE ;
582  return kTRUE ;
583 }
584 
585 
586 
587 ////////////////////////////////////////////////////////////////////////////////
588 /// Stream an object of class RooHistFunc.
589 
590 void RooHistFunc::Streamer(TBuffer &R__b)
591 {
592  if (R__b.IsReading()) {
593  R__b.ReadClassBuffer(RooHistFunc::Class(),this);
594  // WVE - interim solution - fix proxies here
595  _proxyList.Clear() ;
596  registerProxy(_depList) ;
597  } else {
598  R__b.WriteClassBuffer(RooHistFunc::Class(),this);
599  }
600 }
601 
602 
603 ////////////////////////////////////////////////////////////////////////////////
604 /// Schema evolution: if histObsList wasn't filled from persistence (v1)
605 /// then fill it here. Can't be done in regular schema evolution in LinkDef
606 /// as _depList content is not guaranteed to be initialized there
607 
608 void RooHistFunc::ioStreamerPass2()
609 {
610  if (_histObsList.getSize()==0) {
611  _histObsList.addClone(_depList) ;
612  }
613 }
614