Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
RooRealSumFunc.cxx
Go to the documentation of this file.
1 /*****************************************************************************
2  * Project: RooFit *
3  * Package: RooFitCore *
4  * @(#)root/roofitcore:$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 ///
19 /// Class RooRealSumFunc implements a PDF constructed from a sum of
20 /// functions:
21 /// ```
22 /// Sum(i=1,n-1) coef_i * func_i(x) + [ 1 - (Sum(i=1,n-1) coef_i ] * func_n(x)
23 /// pdf(x) = ------------------------------------------------------------------------------
24 /// Sum(i=1,n-1) coef_i * Int(func_i)dx + [ 1 - (Sum(i=1,n-1) coef_i ] * Int(func_n)dx
25 ///
26 /// ```
27 /// where coef_i and func_i are RooAbsReal objects, and x is the collection of dependents.
28 /// In the present version coef_i may not depend on x, but this limitation may be removed in the future
29 ///
30 /// ### Difference between RooAddPdf / RooRealSum{Func|Pdf}
31 /// - RooAddPdf is a PDF of PDFs, *i.e.* its components need to be normalised and non-negative.
32 /// - RooRealSumPdf is a PDF of functions, *i.e.*, its components can be negative, but their sum cannot be. The normalisation
33 /// is computed automatically, unless the PDF is extended (see above).
34 /// - RooRealSumFunc is a sum of functions. It is neither normalised, nor need it be positive.
35 
36 #include "RooFit.h"
37 #include "Riostream.h"
38 
39 #include "TIterator.h"
40 #include "TList.h"
41 #include "RooRealSumFunc.h"
42 #include "RooRealProxy.h"
43 #include "RooPlot.h"
44 #include "RooRealVar.h"
45 #include "RooAddGenContext.h"
46 #include "RooRealConstant.h"
47 #include "RooRealIntegral.h"
48 #include "RooMsgService.h"
49 #include "RooNameReg.h"
50 #include "RooTrace.h"
51 
52 #include <algorithm>
53 #include <memory>
54 
55 using namespace std;
56 
57 ClassImp(RooRealSumFunc);
58 
59 Bool_t RooRealSumFunc::_doFloorGlobal = kFALSE;
60 
61 //_____________________________________________________________________________
62 RooRealSumFunc::RooRealSumFunc()
63 {
64  // Default constructor
65  // coverity[UNINIT_CTOR]
66  _funcIter = _funcList.createIterator();
67  _coefIter = _coefList.createIterator();
68  _doFloor = kFALSE;
69  TRACE_CREATE
70 }
71 
72 //_____________________________________________________________________________
73 RooRealSumFunc::RooRealSumFunc(const char *name, const char *title)
74  : RooAbsReal(name, title), _normIntMgr(this, 10), _haveLastCoef(kFALSE),
75  _funcList("!funcList", "List of functions", this), _coefList("!coefList", "List of coefficients", this),
76  _doFloor(kFALSE)
77 {
78  // Constructor with name and title
79  _funcIter = _funcList.createIterator();
80  _coefIter = _coefList.createIterator();
81  TRACE_CREATE
82 }
83 
84 //_____________________________________________________________________________
85 RooRealSumFunc::RooRealSumFunc(const char *name, const char *title, RooAbsReal &func1, RooAbsReal &func2,
86  RooAbsReal &coef1)
87  : RooAbsReal(name, title), _normIntMgr(this, 10), _haveLastCoef(kFALSE),
88  _funcList("!funcList", "List of functions", this), _coefList("!coefList", "List of coefficients", this),
89  _doFloor(kFALSE)
90 {
91  // Construct p.d.f consisting of coef1*func1 + (1-coef1)*func2
92  // The input coefficients and functions are allowed to be negative
93  // but the resulting sum is not, which is enforced at runtime
94 
95  // Special constructor with two functions and one coefficient
96  _funcIter = _funcList.createIterator();
97  _coefIter = _coefList.createIterator();
98 
99  _funcList.add(func1);
100  _funcList.add(func2);
101  _coefList.add(coef1);
102  TRACE_CREATE
103 }
104 
105 //_____________________________________________________________________________
106 RooRealSumFunc::RooRealSumFunc(const char *name, const char *title, const RooArgList &inFuncList,
107  const RooArgList &inCoefList)
108  : RooAbsReal(name, title), _normIntMgr(this, 10), _haveLastCoef(kFALSE),
109  _funcList("!funcList", "List of functions", this), _coefList("!coefList", "List of coefficients", this),
110  _doFloor(kFALSE)
111 {
112  // Constructor p.d.f implementing sum_i [ coef_i * func_i ], if N_coef==N_func
113  // or sum_i [ coef_i * func_i ] + (1 - sum_i [ coef_i ] )* func_N if Ncoef==N_func-1
114  //
115  // All coefficients and functions are allowed to be negative
116  // but the sum is not, which is enforced at runtime.
117 
118  if (!(inFuncList.getSize() == inCoefList.getSize() + 1 || inFuncList.getSize() == inCoefList.getSize())) {
119  coutE(InputArguments) << "RooRealSumFunc::RooRealSumFunc(" << GetName()
120  << ") number of pdfs and coefficients inconsistent, must have Nfunc=Ncoef or Nfunc=Ncoef+1"
121  << endl;
122  assert(0);
123  }
124 
125  _funcIter = _funcList.createIterator();
126  _coefIter = _coefList.createIterator();
127 
128  // Constructor with N functions and N or N-1 coefs
129  TIterator *funcIter = inFuncList.createIterator();
130  TIterator *coefIter = inCoefList.createIterator();
131  RooAbsArg *func;
132  RooAbsArg *coef;
133 
134  while ((coef = (RooAbsArg *)coefIter->Next())) {
135  func = (RooAbsArg *)funcIter->Next();
136 
137  if (!dynamic_cast<RooAbsReal *>(coef)) {
138  coutW(InputArguments) << "RooRealSumFunc::RooRealSumFunc(" << GetName() << ") coefficient " << coef->GetName()
139  << " is not of type RooAbsReal, ignored" << endl;
140  continue;
141  }
142  if (!dynamic_cast<RooAbsReal *>(func)) {
143  coutW(InputArguments) << "RooRealSumFunc::RooRealSumFunc(" << GetName() << ") func " << func->GetName()
144  << " is not of type RooAbsReal, ignored" << endl;
145  continue;
146  }
147  _funcList.add(*func);
148  _coefList.add(*coef);
149  }
150 
151  func = (RooAbsReal *)funcIter->Next();
152  if (func) {
153  if (!dynamic_cast<RooAbsReal *>(func)) {
154  coutE(InputArguments) << "RooRealSumFunc::RooRealSumFunc(" << GetName() << ") last func " << coef->GetName()
155  << " is not of type RooAbsReal, fatal error" << endl;
156  assert(0);
157  }
158  _funcList.add(*func);
159  } else {
160  _haveLastCoef = kTRUE;
161  }
162 
163  delete funcIter;
164  delete coefIter;
165  TRACE_CREATE
166 }
167 
168 //_____________________________________________________________________________
169 RooRealSumFunc::RooRealSumFunc(const RooRealSumFunc &other, const char *name)
170  : RooAbsReal(other, name), _normIntMgr(other._normIntMgr, this), _haveLastCoef(other._haveLastCoef),
171  _funcList("!funcList", this, other._funcList), _coefList("!coefList", this, other._coefList),
172  _doFloor(other._doFloor)
173 {
174  // Copy constructor
175 
176  _funcIter = _funcList.createIterator();
177  _coefIter = _coefList.createIterator();
178  TRACE_CREATE
179 }
180 
181 //_____________________________________________________________________________
182 RooRealSumFunc::~RooRealSumFunc()
183 {
184  // Destructor
185  delete _funcIter;
186  delete _coefIter;
187 
188  TRACE_DESTROY
189 }
190 
191 //_____________________________________________________________________________
192 Double_t RooRealSumFunc::evaluate() const
193 {
194  // Calculate the current value
195 
196  Double_t value(0);
197 
198  // Do running sum of coef/func pairs, calculate lastCoef.
199  RooFIter funcIter = _funcList.fwdIterator();
200  RooFIter coefIter = _coefList.fwdIterator();
201  RooAbsReal *coef;
202  RooAbsReal *func;
203 
204  // N funcs, N-1 coefficients
205  Double_t lastCoef(1);
206  while ((coef = (RooAbsReal *)coefIter.next())) {
207  func = (RooAbsReal *)funcIter.next();
208  Double_t coefVal = coef->getVal();
209  if (coefVal) {
210  cxcoutD(Eval) << "RooRealSumFunc::eval(" << GetName() << ") coefVal = " << coefVal
211  << " funcVal = " << func->IsA()->GetName() << "::" << func->GetName() << " = " << func->getVal()
212  << endl;
213  if (func->isSelectedComp()) {
214  value += func->getVal() * coefVal;
215  }
216  lastCoef -= coef->getVal();
217  }
218  }
219 
220  if (!_haveLastCoef) {
221  // Add last func with correct coefficient
222  func = (RooAbsReal *)funcIter.next();
223  if (func->isSelectedComp()) {
224  value += func->getVal() * lastCoef;
225  }
226 
227  cxcoutD(Eval) << "RooRealSumFunc::eval(" << GetName() << ") lastCoef = " << lastCoef
228  << " funcVal = " << func->getVal() << endl;
229 
230  // Warn about coefficient degeneration
231  if (lastCoef < 0 || lastCoef > 1) {
232  coutW(Eval) << "RooRealSumFunc::evaluate(" << GetName()
233  << " WARNING: sum of FUNC coefficients not in range [0-1], value=" << 1 - lastCoef << endl;
234  }
235  }
236 
237  // Introduce floor if so requested
238  if (value < 0 && (_doFloor || _doFloorGlobal)) {
239  value = 0;
240  }
241 
242  return value;
243 }
244 
245 //_____________________________________________________________________________
246 Bool_t RooRealSumFunc::checkObservables(const RooArgSet *nset) const
247 {
248  // Check if FUNC is valid for given normalization set.
249  // Coeffient and FUNC must be non-overlapping, but func-coefficient
250  // pairs may overlap each other
251  //
252  // In the present implementation, coefficients may not be observables or derive
253  // from observables
254 
255  Bool_t ret(kFALSE);
256 
257  _funcIter->Reset();
258  _coefIter->Reset();
259  RooAbsReal *coef;
260  RooAbsReal *func;
261  while ((coef = (RooAbsReal *)_coefIter->Next())) {
262  func = (RooAbsReal *)_funcIter->Next();
263  if (func->observableOverlaps(nset, *coef)) {
264  coutE(InputArguments) << "RooRealSumFunc::checkObservables(" << GetName() << "): ERROR: coefficient "
265  << coef->GetName() << " and FUNC " << func->GetName()
266  << " have one or more observables in common" << endl;
267  ret = kTRUE;
268  }
269  if (coef->dependsOn(*nset)) {
270  coutE(InputArguments) << "RooRealPdf::checkObservables(" << GetName() << "): ERROR coefficient "
271  << coef->GetName() << " depends on one or more of the following observables";
272  nset->Print("1");
273  ret = kTRUE;
274  }
275  }
276 
277  return ret;
278 }
279 
280 //_____________________________________________________________________________
281 Int_t RooRealSumFunc::getAnalyticalIntegralWN(RooArgSet &allVars, RooArgSet &analVars, const RooArgSet *normSet2,
282  const char *rangeName) const
283 {
284  // cout <<
285  // "RooRealSumFunc::getAnalyticalIntegralWN:"<<GetName()<<"("<<allVars<<",analVars,"<<(normSet2?*normSet2:RooArgSet())<<","<<(rangeName?rangeName:"<none>")
286  // << endl;
287  // Advertise that all integrals can be handled internally.
288 
289  // Handle trivial no-integration scenario
290  if (allVars.getSize() == 0)
291  return 0;
292  if (_forceNumInt)
293  return 0;
294 
295  // Select subset of allVars that are actual dependents
296  analVars.add(allVars);
297  RooArgSet *normSet = normSet2 ? getObservables(normSet2) : 0;
298 
299  // Check if this configuration was created before
300  Int_t sterileIdx(-1);
301  CacheElem *cache = (CacheElem *)_normIntMgr.getObj(normSet, &analVars, &sterileIdx, RooNameReg::ptr(rangeName));
302  if (cache) {
303  // cout <<
304  // "RooRealSumFunc("<<this<<")::getAnalyticalIntegralWN:"<<GetName()<<"("<<allVars<<","<<analVars<<","<<(normSet2?*normSet2:RooArgSet())<<","<<(rangeName?rangeName:"<none>")
305  // << " -> " << _normIntMgr.lastIndex()+1 << " (cached)" << endl;
306  return _normIntMgr.lastIndex() + 1;
307  }
308 
309  // Create new cache element
310  cache = new CacheElem;
311 
312  // Make list of function projection and normalization integrals
313  _funcIter->Reset();
314  RooAbsReal *func;
315  while ((func = (RooAbsReal *)_funcIter->Next())) {
316  RooAbsReal *funcInt = func->createIntegral(analVars, rangeName);
317  if(funcInt->InheritsFrom(RooRealIntegral::Class())) ((RooRealIntegral*)funcInt)->setAllowComponentSelection(true);
318  cache->_funcIntList.addOwned(*funcInt);
319  if (normSet && normSet->getSize() > 0) {
320  RooAbsReal *funcNorm = func->createIntegral(*normSet);
321  cache->_funcNormList.addOwned(*funcNorm);
322  }
323  }
324 
325  // Store cache element
326  Int_t code = _normIntMgr.setObj(normSet, &analVars, (RooAbsCacheElement *)cache, RooNameReg::ptr(rangeName));
327 
328  if (normSet) {
329  delete normSet;
330  }
331 
332  // cout <<
333  // "RooRealSumFunc("<<this<<")::getAnalyticalIntegralWN:"<<GetName()<<"("<<allVars<<","<<analVars<<","<<(normSet2?*normSet2:RooArgSet())<<","<<(rangeName?rangeName:"<none>")
334  // << " -> " << code+1 << endl;
335  return code + 1;
336 }
337 
338 //_____________________________________________________________________________
339 Double_t RooRealSumFunc::analyticalIntegralWN(Int_t code, const RooArgSet *normSet2, const char *rangeName) const
340 {
341  // cout <<
342  // "RooRealSumFunc::analyticalIntegralWN:"<<GetName()<<"("<<code<<","<<(normSet2?*normSet2:RooArgSet())<<","<<(rangeName?rangeName:"<none>")
343  // << endl;
344  // Implement analytical integrations by deferring integration of component
345  // functions to integrators of components
346 
347  // Handle trivial passthrough scenario
348  if (code == 0)
349  return getVal(normSet2);
350 
351  // WVE needs adaptation for rangeName feature
352  CacheElem *cache = (CacheElem *)_normIntMgr.getObjByIndex(code - 1);
353  if (cache == 0) { // revive the (sterilized) cache
354  // cout <<
355  // "RooRealSumFunc("<<this<<")::analyticalIntegralWN:"<<GetName()<<"("<<code<<","<<(normSet2?*normSet2:RooArgSet())<<","<<(rangeName?rangeName:"<none>")
356  // << ": reviving cache "<< endl;
357  std::unique_ptr<RooArgSet> vars(getParameters(RooArgSet()));
358  std::unique_ptr<RooArgSet> iset(_normIntMgr.nameSet2ByIndex(code - 1)->select(*vars));
359  std::unique_ptr<RooArgSet> nset(_normIntMgr.nameSet1ByIndex(code - 1)->select(*vars));
360  RooArgSet dummy;
361  Int_t code2 = getAnalyticalIntegralWN(*iset, dummy, nset.get(), rangeName);
362  assert(code == code2); // must have revived the right (sterilized) slot...
363  (void)code2;
364  cache = (CacheElem *)_normIntMgr.getObjByIndex(code - 1);
365  assert(cache != 0);
366  }
367 
368  RooFIter funcIntIter = cache->_funcIntList.fwdIterator();
369  RooFIter coefIter = _coefList.fwdIterator();
370  RooFIter funcIter = _funcList.fwdIterator();
371  RooAbsReal *coef(0), *funcInt(0), *func(0);
372  Double_t value(0);
373 
374  // N funcs, N-1 coefficients
375  Double_t lastCoef(1);
376  while ((coef = (RooAbsReal *)coefIter.next())) {
377  funcInt = (RooAbsReal *)funcIntIter.next();
378  func = (RooAbsReal *)funcIter.next();
379  Double_t coefVal = coef->getVal(normSet2);
380  if (coefVal) {
381  assert(func);
382  if (normSet2 == 0 || func->isSelectedComp()) {
383  assert(funcInt);
384  value += funcInt->getVal() * coefVal;
385  }
386  lastCoef -= coef->getVal(normSet2);
387  }
388  }
389 
390  if (!_haveLastCoef) {
391  // Add last func with correct coefficient
392  funcInt = (RooAbsReal *)funcIntIter.next();
393  if (normSet2 == 0 || func->isSelectedComp()) {
394  assert(funcInt);
395  value += funcInt->getVal() * lastCoef;
396  }
397 
398  // Warn about coefficient degeneration
399  if (lastCoef < 0 || lastCoef > 1) {
400  coutW(Eval) << "RooRealSumFunc::evaluate(" << GetName()
401  << " WARNING: sum of FUNC coefficients not in range [0-1], value=" << 1 - lastCoef << endl;
402  }
403  }
404 
405  Double_t normVal(1);
406  if (normSet2 && normSet2->getSize() > 0) {
407  normVal = 0;
408 
409  // N funcs, N-1 coefficients
410  RooAbsReal *funcNorm;
411  RooFIter funcNormIter = cache->_funcNormList.fwdIterator();
412  RooFIter coefIter2 = _coefList.fwdIterator();
413  while ((coef = (RooAbsReal *)coefIter2.next())) {
414  funcNorm = (RooAbsReal *)funcNormIter.next();
415  Double_t coefVal = coef->getVal(normSet2);
416  if (coefVal) {
417  assert(funcNorm);
418  normVal += funcNorm->getVal() * coefVal;
419  }
420  }
421 
422  // Add last func with correct coefficient
423  if (!_haveLastCoef) {
424  funcNorm = (RooAbsReal *)funcNormIter.next();
425  assert(funcNorm);
426  normVal += funcNorm->getVal() * lastCoef;
427  }
428  }
429 
430  return value / normVal;
431 }
432 
433 //_____________________________________________________________________________
434 std::list<Double_t> *RooRealSumFunc::binBoundaries(RooAbsRealLValue &obs, Double_t xlo, Double_t xhi) const
435 {
436  list<Double_t> *sumBinB = 0;
437  Bool_t needClean(kFALSE);
438 
439  RooFIter iter = _funcList.fwdIterator();
440  RooAbsReal *func;
441  // Loop over components pdf
442  while ((func = (RooAbsReal *)iter.next())) {
443 
444  list<Double_t> *funcBinB = func->binBoundaries(obs, xlo, xhi);
445 
446  // Process hint
447  if (funcBinB) {
448  if (!sumBinB) {
449  // If this is the first hint, then just save it
450  sumBinB = funcBinB;
451  } else {
452 
453  list<Double_t> *newSumBinB = new list<Double_t>(sumBinB->size() + funcBinB->size());
454 
455  // Merge hints into temporary array
456  merge(funcBinB->begin(), funcBinB->end(), sumBinB->begin(), sumBinB->end(), newSumBinB->begin());
457 
458  // Copy merged array without duplicates to new sumBinBArrau
459  delete sumBinB;
460  delete funcBinB;
461  sumBinB = newSumBinB;
462  needClean = kTRUE;
463  }
464  }
465  }
466 
467  // Remove consecutive duplicates
468  if (needClean) {
469  list<Double_t>::iterator new_end = unique(sumBinB->begin(), sumBinB->end());
470  sumBinB->erase(new_end, sumBinB->end());
471  }
472 
473  return sumBinB;
474 }
475 
476 //_____________________________________________________________________________B
477 Bool_t RooRealSumFunc::isBinnedDistribution(const RooArgSet &obs) const
478 {
479  // If all components that depend on obs are binned that so is the product
480 
481  RooFIter iter = _funcList.fwdIterator();
482  RooAbsReal *func;
483  while ((func = (RooAbsReal *)iter.next())) {
484  if (func->dependsOn(obs) && !func->isBinnedDistribution(obs)) {
485  return kFALSE;
486  }
487  }
488 
489  return kTRUE;
490 }
491 
492 //_____________________________________________________________________________
493 std::list<Double_t> *RooRealSumFunc::plotSamplingHint(RooAbsRealLValue &obs, Double_t xlo, Double_t xhi) const
494 {
495  list<Double_t> *sumHint = 0;
496  Bool_t needClean(kFALSE);
497 
498  RooFIter iter = _funcList.fwdIterator();
499  RooAbsReal *func;
500  // Loop over components pdf
501  while ((func = (RooAbsReal *)iter.next())) {
502 
503  list<Double_t> *funcHint = func->plotSamplingHint(obs, xlo, xhi);
504 
505  // Process hint
506  if (funcHint) {
507  if (!sumHint) {
508 
509  // If this is the first hint, then just save it
510  sumHint = funcHint;
511 
512  } else {
513 
514  list<Double_t> *newSumHint = new list<Double_t>(sumHint->size() + funcHint->size());
515 
516  // Merge hints into temporary array
517  merge(funcHint->begin(), funcHint->end(), sumHint->begin(), sumHint->end(), newSumHint->begin());
518 
519  // Copy merged array without duplicates to new sumHintArrau
520  delete sumHint;
521  sumHint = newSumHint;
522  needClean = kTRUE;
523  }
524  }
525  }
526 
527  // Remove consecutive duplicates
528  if (needClean) {
529  list<Double_t>::iterator new_end = unique(sumHint->begin(), sumHint->end());
530  sumHint->erase(new_end, sumHint->end());
531  }
532 
533  return sumHint;
534 }
535 
536 //_____________________________________________________________________________
537 void RooRealSumFunc::setCacheAndTrackHints(RooArgSet &trackNodes)
538 {
539  // Label OK'ed components of a RooRealSumFunc with cache-and-track
540  RooFIter siter = funcList().fwdIterator();
541  RooAbsArg *sarg;
542  while ((sarg = siter.next())) {
543  if (sarg->canNodeBeCached() == Always) {
544  trackNodes.add(*sarg);
545  // cout << "tracking node RealSumFunc component " << sarg->IsA()->GetName() << "::" << sarg->GetName() << endl
546  // ;
547  }
548  }
549 }
550 
551 //_____________________________________________________________________________
552 void RooRealSumFunc::printMetaArgs(ostream &os) const
553 {
554  // Customized printing of arguments of a RooRealSumFuncy to more intuitively reflect the contents of the
555  // product operator construction
556 
557  _funcIter->Reset();
558  _coefIter->Reset();
559 
560  Bool_t first(kTRUE);
561 
562  RooAbsArg *coef, *func;
563  if (_coefList.getSize() != 0) {
564  while ((coef = (RooAbsArg *)_coefIter->Next())) {
565  if (!first) {
566  os << " + ";
567  } else {
568  first = kFALSE;
569  }
570  func = (RooAbsArg *)_funcIter->Next();
571  os << coef->GetName() << " * " << func->GetName();
572  }
573  func = (RooAbsArg *)_funcIter->Next();
574  if (func) {
575  os << " + [%] * " << func->GetName();
576  }
577  } else {
578 
579  while ((func = (RooAbsArg *)_funcIter->Next())) {
580  if (!first) {
581  os << " + ";
582  } else {
583  first = kFALSE;
584  }
585  os << func->GetName();
586  }
587  }
588 
589  os << " ";
590 }