Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
RooProduct.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  * GR, Gerhard Raven, VU Amsterdan, graven@nikhef.nl *
8  * *
9  * Copyright (c) 2000-2007, 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 RooProduct.cxx
19 \class RooProduct
20 \ingroup Roofitcore
21 
22 A RooProduct represents the product of a given set of RooAbsReal objects.
23 
24 **/
25 
26 
27 #include <cmath>
28 #include <memory>
29 
30 #include "RooProduct.h"
31 #include "RooNameReg.h"
32 #include "RooAbsReal.h"
33 #include "RooAbsCategory.h"
34 #include "RooErrorHandler.h"
35 #include "RooMsgService.h"
36 #include "RooTrace.h"
37 
38 using namespace std ;
39 
40 ClassImp(RooProduct);
41 ;
42 
43 class RooProduct::ProdMap : public std::vector<std::pair<RooArgSet*,RooArgList*> > {} ;
44 
45 // Namespace with helper functions that have STL stuff that we don't want to expose to CINT
46 namespace {
47  typedef RooProduct::ProdMap::iterator RPPMIter ;
48  std::pair<RPPMIter,RPPMIter> findOverlap2nd(RPPMIter i, RPPMIter end) ;
49  void dump_map(ostream& os, RPPMIter i, RPPMIter end) ;
50 }
51 
52 
53 
54 ////////////////////////////////////////////////////////////////////////////////
55 /// Default constructor
56 
57 RooProduct::RooProduct()
58 {
59  TRACE_CREATE
60 }
61 
62 
63 
64 ////////////////////////////////////////////////////////////////////////////////
65 /// Destructor
66 
67 RooProduct::~RooProduct()
68 {
69  TRACE_DESTROY
70 }
71 
72 
73 
74 ////////////////////////////////////////////////////////////////////////////////
75 /// Construct function representing the product of functions in prodSet
76 
77 RooProduct::RooProduct(const char* name, const char* title, const RooArgList& prodSet) :
78  RooAbsReal(name, title),
79  _compRSet("!compRSet","Set of real product components",this),
80  _compCSet("!compCSet","Set of category product components",this),
81  _cacheMgr(this,10)
82 {
83  for (auto comp : prodSet) {
84  if (dynamic_cast<RooAbsReal*>(comp)) {
85  _compRSet.add(*comp) ;
86  } else if (dynamic_cast<RooAbsCategory*>(comp)) {
87  _compCSet.add(*comp) ;
88  } else {
89  coutE(InputArguments) << "RooProduct::ctor(" << GetName() << ") ERROR: component " << comp->GetName()
90  << " is not of type RooAbsReal or RooAbsCategory" << endl ;
91  RooErrorHandler::softAbort() ;
92  }
93  }
94  TRACE_CREATE
95 }
96 
97 
98 
99 ////////////////////////////////////////////////////////////////////////////////
100 /// Copy constructor
101 
102 RooProduct::RooProduct(const RooProduct& other, const char* name) :
103  RooAbsReal(other, name),
104  _compRSet("!compRSet",this,other._compRSet),
105  _compCSet("!compCSet",this,other._compCSet),
106  _cacheMgr(other._cacheMgr,this)
107 {
108  TRACE_CREATE
109 }
110 
111 
112 
113 ////////////////////////////////////////////////////////////////////////////////
114 /// Force internal handling of integration of given observable if any
115 /// of the product terms depend on it.
116 
117 Bool_t RooProduct::forceAnalyticalInt(const RooAbsArg& dep) const
118 {
119  // Force internal handling of integration of given observable if any
120  // of the product terms depend on it.
121 
122  RooFIter compRIter = _compRSet.fwdIterator() ;
123  RooAbsReal* rcomp ;
124  Bool_t depends(kFALSE);
125  while((rcomp=(RooAbsReal*)compRIter.next())&&!depends) {
126  depends = rcomp->dependsOn(dep);
127  }
128  return depends ;
129 }
130 
131 
132 
133 ////////////////////////////////////////////////////////////////////////////////
134 /// Group observables into subsets in which the product factorizes
135 /// and that can thus be integrated separately
136 
137 RooProduct::ProdMap* RooProduct::groupProductTerms(const RooArgSet& allVars) const
138 {
139  ProdMap* map = new ProdMap ;
140 
141  // Do we have any terms which do not depend on the
142  // on the variables we integrate over?
143  RooAbsReal* rcomp ;
144  RooFIter compRIter = _compRSet.fwdIterator() ;
145  RooArgList *indep = new RooArgList();
146  while((rcomp=(RooAbsReal*) compRIter.next())) {
147  if( !rcomp->dependsOn(allVars) ) indep->add(*rcomp);
148  }
149  if (indep->getSize()!=0) {
150  map->push_back( std::make_pair(new RooArgSet(),indep) );
151  }
152 
153  // Map observables -> functions ; start with individual observables
154  RooFIter allVarsIter = allVars.fwdIterator() ;
155  RooAbsReal* var ;
156  while((var=(RooAbsReal*)allVarsIter.next())) {
157  RooArgSet *vars = new RooArgSet(); vars->add(*var);
158  RooArgList *comps = new RooArgList();
159  RooAbsReal* rcomp2 ;
160 
161  compRIter = _compRSet.fwdIterator() ;
162  while((rcomp2=(RooAbsReal*) compRIter.next())) {
163  if( rcomp2->dependsOn(*var) ) comps->add(*rcomp2);
164  }
165  map->push_back( std::make_pair(vars,comps) );
166  }
167 
168  // Merge groups with overlapping dependents
169  Bool_t overlap;
170  do {
171  std::pair<ProdMap::iterator,ProdMap::iterator> i = findOverlap2nd(map->begin(),map->end());
172  overlap = (i.first!=i.second);
173  if (overlap) {
174  i.first->first->add(*i.second->first);
175 
176  // In the merging step, make sure not to duplicate
177  RooFIter it = i.second->second->fwdIterator() ;
178  RooAbsArg* targ ;
179  while ((targ = it.next())) {
180  if (!i.first->second->find(*targ)) {
181  i.first->second->add(*targ) ;
182  }
183  }
184  //i.first->second->add(*i.second->second);
185 
186  delete i.second->first;
187  delete i.second->second;
188  map->erase(i.second);
189  }
190  } while (overlap);
191 
192  // check that we have all variables to be integrated over on the LHS
193  // of the map, and all terms in the product do appear on the RHS
194  int nVar=0; int nFunc=0;
195  for (ProdMap::iterator i = map->begin();i!=map->end();++i) {
196  nVar+=i->first->getSize();
197  nFunc+=i->second->getSize();
198  }
199  assert(nVar==allVars.getSize());
200  assert(nFunc==_compRSet.getSize());
201  return map;
202 }
203 
204 
205 
206 ////////////////////////////////////////////////////////////////////////////////
207 /// Return list of (partial) integrals whose product defines the integral of this
208 /// RooProduct over the observables in iset in range isetRange. If no such list
209 /// exists, create it now and store it in the cache for future use.
210 
211 Int_t RooProduct::getPartIntList(const RooArgSet* iset, const char *isetRange) const
212 {
213 
214  // check if we already have integrals for this combination of factors
215  Int_t sterileIndex(-1);
216  CacheElem* cache = (CacheElem*) _cacheMgr.getObj(iset,iset,&sterileIndex,RooNameReg::ptr(isetRange));
217  if (cache!=0) {
218  Int_t code = _cacheMgr.lastIndex();
219  return code;
220  }
221 
222  ProdMap* map = groupProductTerms(*iset);
223 
224  cxcoutD(Integration) << "RooProduct::getPartIntList(" << GetName() << ") groupProductTerms returned map" ;
225  if (dologD(Integration)) {
226  dump_map(ccoutD(Integration),map->begin(),map->end());
227  ccoutD(Integration) << endl;
228  }
229 
230  // did we find any factorizable terms?
231  if (map->size()<2) {
232 
233  for (ProdMap::iterator iter = map->begin() ; iter != map->end() ; ++iter) {
234  delete iter->first ;
235  delete iter->second ;
236  }
237 
238  delete map ;
239  return -1; // RRI caller will zero analVars if return code = 0....
240  }
241  cache = new CacheElem();
242 
243  for (ProdMap::const_iterator i = map->begin();i!=map->end();++i) {
244  RooAbsReal *term(0);
245  if (i->second->getSize()>1) { // create a RooProd for this subexpression
246  const char *name = makeFPName("SUBPROD_",*i->second);
247  term = new RooProduct(name,name,*i->second);
248  cache->_ownedList.addOwned(*term);
249  cxcoutD(Integration) << "RooProduct::getPartIntList(" << GetName() << ") created subexpression " << term->GetName() << endl;
250  } else {
251  assert(i->second->getSize()==1);
252  RooFIter j = i->second->fwdIterator();
253  term = (RooAbsReal*)j.next();
254  }
255  assert(term!=0);
256  if (i->first->getSize()==0) { // check whether we need to integrate over this term or not...
257  cache->_prodList.add(*term);
258  cxcoutD(Integration) << "RooProduct::getPartIntList(" << GetName() << ") adding simple factor " << term->GetName() << endl;
259  } else {
260  RooAbsReal *integral = term->createIntegral(*i->first,isetRange);
261  cache->_prodList.add(*integral);
262  cache->_ownedList.addOwned(*integral);
263  cxcoutD(Integration) << "RooProduct::getPartIntList(" << GetName() << ") adding integral for " << term->GetName() << " : " << integral->GetName() << endl;
264  }
265  }
266  // add current set-up to cache, and return index..
267  Int_t code = _cacheMgr.setObj(iset,iset,(RooAbsCacheElement*)cache,RooNameReg::ptr(isetRange));
268 
269  cxcoutD(Integration) << "RooProduct::getPartIntList(" << GetName() << ") created list " << cache->_prodList << " with code " << code+1 << endl
270  << " for iset=" << *iset << " @" << iset << " range: " << (isetRange?isetRange:"<none>") << endl ;
271 
272  for (ProdMap::iterator iter = map->begin() ; iter != map->end() ; ++iter) {
273  delete iter->first ;
274  delete iter->second ;
275  }
276  delete map ;
277  return code;
278 }
279 
280 
281 ////////////////////////////////////////////////////////////////////////////////
282 /// Declare that we handle all integrations internally
283 
284 Int_t RooProduct::getAnalyticalIntegralWN(RooArgSet& allVars, RooArgSet& analVars,
285  const RooArgSet* /*normSet*/,
286  const char* rangeName) const
287 {
288  if (_forceNumInt) return 0 ;
289 
290  // Declare that we can analytically integrate all requested observables
291  // (basically, we will take care of the problem, and delegate where required)
292  //assert(normSet==0);
293  assert(analVars.getSize()==0);
294  analVars.add(allVars) ;
295  Int_t code = getPartIntList(&analVars,rangeName)+1;
296  return code ;
297 }
298 
299 
300 ////////////////////////////////////////////////////////////////////////////////
301 /// Calculate integral internally from appropriate partial integral cache
302 
303 Double_t RooProduct::analyticalIntegral(Int_t code, const char* rangeName) const
304 {
305  // note: rangeName implicit encoded in code: see _cacheMgr.setObj in getPartIntList...
306  CacheElem *cache = (CacheElem*) _cacheMgr.getObjByIndex(code-1);
307  if (cache==0) {
308  // cache got sterilized, trigger repopulation of this slot, then try again...
309  std::unique_ptr<RooArgSet> vars( getParameters(RooArgSet()) );
310  std::unique_ptr<RooArgSet> iset( _cacheMgr.nameSet2ByIndex(code-1)->select(*vars) );
311  Int_t code2 = getPartIntList(iset.get(),rangeName)+1;
312  assert(code==code2); // must have revived the right (sterilized) slot...
313  return analyticalIntegral(code2,rangeName);
314  }
315  assert(cache!=0);
316 
317  return calculate(cache->_prodList);
318 }
319 
320 
321 ////////////////////////////////////////////////////////////////////////////////
322 /// Calculate and return product of partial terms in partIntList
323 
324 Double_t RooProduct::calculate(const RooArgList& partIntList) const
325 {
326  RooAbsReal *term(0);
327  Double_t val=1;
328  RooFIter i = partIntList.fwdIterator() ;
329  while((term=(RooAbsReal*)i.next())) {
330  double x = term->getVal();
331  val*= x;
332  }
333  return val;
334 }
335 
336 
337 ////////////////////////////////////////////////////////////////////////////////
338 /// Construct automatic name for internal product terms
339 
340 const char* RooProduct::makeFPName(const char *pfx,const RooArgSet& terms) const
341 {
342  static TString pname;
343  pname = pfx;
344  RooFIter i = terms.fwdIterator();
345  RooAbsArg *arg;
346  Bool_t first(kTRUE);
347  while((arg=(RooAbsArg*)i.next())) {
348  if (first) { first=kFALSE;}
349  else pname.Append("_X_");
350  pname.Append(arg->GetName());
351  }
352  return pname.Data();
353 }
354 
355 
356 
357 ////////////////////////////////////////////////////////////////////////////////
358 /// Evaluate product of input functions
359 
360 Double_t RooProduct::evaluate() const
361 {
362  Double_t prod(1) ;
363 
364  const RooArgSet* nset = _compRSet.nset() ;
365  for (const auto item : _compRSet) {
366  auto rcomp = static_cast<const RooAbsReal*>(item);
367 
368  prod *= rcomp->getVal(nset) ;
369  }
370 
371  for (const auto item : _compCSet) {
372  auto ccomp = static_cast<const RooAbsCategory*>(item);
373 
374  prod *= ccomp->getIndex() ;
375  }
376 
377  return prod ;
378 }
379 
380 
381 
382 ////////////////////////////////////////////////////////////////////////////////
383 /// Forward the plot sampling hint from the p.d.f. that defines the observable obs
384 
385 std::list<Double_t>* RooProduct::binBoundaries(RooAbsRealLValue& obs, Double_t xlo, Double_t xhi) const
386 {
387  for (const auto item : _compRSet) {
388  auto func = static_cast<const RooAbsReal*>(item);
389 
390  list<Double_t>* binb = func->binBoundaries(obs,xlo,xhi) ;
391  if (binb) {
392  return binb ;
393  }
394  }
395 
396  return 0 ;
397 }
398 
399 
400 //_____________________________________________________________________________B
401 Bool_t RooProduct::isBinnedDistribution(const RooArgSet& obs) const
402 {
403  // If all components that depend on obs are binned that so is the product
404 
405  for (const auto item : _compRSet) {
406  auto func = static_cast<const RooAbsReal*>(item);
407 
408  if (func->dependsOn(obs) && !func->isBinnedDistribution(obs)) {
409  return kFALSE ;
410  }
411  }
412 
413  return kTRUE ;
414 }
415 
416 
417 
418 ////////////////////////////////////////////////////////////////////////////////
419 /// Forward the plot sampling hint from the p.d.f. that defines the observable obs
420 
421 std::list<Double_t>* RooProduct::plotSamplingHint(RooAbsRealLValue& obs, Double_t xlo, Double_t xhi) const
422 {
423  for (const auto item : _compRSet) {
424  auto func = static_cast<const RooAbsReal*>(item);
425 
426  list<Double_t>* hint = func->plotSamplingHint(obs,xlo,xhi) ;
427  if (hint) {
428  return hint ;
429  }
430  }
431 
432  return 0 ;
433 }
434 
435 
436 
437 ////////////////////////////////////////////////////////////////////////////////
438 /// Destructor
439 
440 RooProduct::CacheElem::~CacheElem()
441 {
442 }
443 
444 
445 ////////////////////////////////////////////////////////////////////////////////
446 /// Return list of all RooAbsArgs in cache element
447 
448 RooArgList RooProduct::CacheElem::containedArgs(Action)
449 {
450  RooArgList ret(_ownedList) ;
451  return ret ;
452 }
453 
454 
455 
456 
457 ////////////////////////////////////////////////////////////////////////////////
458 /// Label OK'ed components of a RooProduct with cache-and-track
459 
460 void RooProduct::setCacheAndTrackHints(RooArgSet& trackNodes)
461 {
462  RooArgSet comp(components()) ;
463  for (const auto parg : comp) {
464  if (parg->isDerived()) {
465  if (parg->canNodeBeCached()==Always) {
466  trackNodes.add(*parg) ;
467  //cout << "tracking node RooProduct component " << parg->IsA()->GetName() << "::" << parg->GetName() << endl ;
468  }
469  }
470  }
471 }
472 
473 
474 
475 
476 
477 ////////////////////////////////////////////////////////////////////////////////
478 /// Customized printing of arguments of a RooProduct to more intuitively reflect the contents of the
479 /// product operator construction
480 
481 void RooProduct::printMetaArgs(ostream& os) const
482 {
483  Bool_t first(kTRUE) ;
484 
485  for (const auto rcomp : _compRSet) {
486  if (!first) { os << " * " ; } else { first = kFALSE ; }
487  os << rcomp->GetName() ;
488  }
489 
490  for (const auto item : _compCSet) {
491  auto ccomp = static_cast<const RooAbsCategory*>(item);
492 
493  if (!first) { os << " * " ; } else { first = kFALSE ; }
494  os << ccomp->GetName() ;
495  }
496 
497  os << " " ;
498 }
499 
500 
501 
502 
503 
504 namespace {
505 
506 std::pair<RPPMIter,RPPMIter> findOverlap2nd(RPPMIter i, RPPMIter end)
507 {
508  // Utility function finding pairs of overlapping input functions
509  for (; i!=end; ++i) for ( RPPMIter j(i+1); j!=end; ++j) {
510  if (i->second->overlaps(*j->second)) {
511  return std::make_pair(i,j);
512  }
513  }
514  return std::make_pair(end,end);
515 }
516 
517 
518 void dump_map(ostream& os, RPPMIter i, RPPMIter end)
519 {
520  // Utility dump function for debugging
521  Bool_t first(kTRUE);
522  os << " [ " ;
523  for(; i!=end;++i) {
524  if (first) { first=kFALSE; }
525  else { os << " , " ; }
526  os << *(i->first) << " -> " << *(i->second) ;
527  }
528  os << " ] " ;
529 }
530 
531 }
532 
533 
534 
535