Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
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
22Represents the product of a given set of RooAbsReal objects.
23**/
24
25#include "RooConstVar.h"
26#include "RooProduct.h"
27
28#include "RooNameReg.h"
29#include "RooAbsReal.h"
30#include "RooAbsCategory.h"
31#include "RooMsgService.h"
32#include "RooTrace.h"
33
34#include <cmath>
35#include <memory>
36
37
38class RooProduct::ProdMap : public std::vector<std::pair<RooArgSet*,RooArgList*> > {} ;
39
40// Namespace with helper functions that have STL stuff that we don't want to expose to CINT
41namespace {
42 typedef RooProduct::ProdMap::iterator RPPMIter ;
43 std::pair<RPPMIter,RPPMIter> findOverlap2nd(RPPMIter i, RPPMIter end) ;
44 void dump_map(std::ostream& os, RPPMIter i, RPPMIter end) ;
45}
46
47
48////////////////////////////////////////////////////////////////////////////////
49/// Default constructor
50
52{
54}
55
56
57////////////////////////////////////////////////////////////////////////////////
58/// Destructor
59
64
65
66
67////////////////////////////////////////////////////////////////////////////////
68/// Construct function representing the product of functions in prodSet
69
70RooProduct::RooProduct(const char* name, const char* title, const RooArgList& prodSet) :
71 RooAbsReal(name, title),
72 _compRSet("!compRSet","Set of real product components",this),
73 _compCSet("!compCSet","Set of category product components",this),
74 _cacheMgr(this,10)
75{
76 for (auto comp : prodSet) {
78 }
80}
81
82
83RooProduct::RooProduct(const char *name, const char *title, RooAbsReal& real1, RooAbsReal& real2) :
84 RooProduct{name, title, {real1, real2}} {}
85
86
87////////////////////////////////////////////////////////////////////////////////
88/// Copy constructor
89
92 _compRSet("!compRSet",this,other._compRSet),
93 _compCSet("!compCSet",this,other._compCSet),
94 _cacheMgr(other._cacheMgr,this)
95{
97}
98
99
100////////////////////////////////////////////////////////////////////////////////
101/// Add a term to this product.
103 if (dynamic_cast<RooAbsReal*>(term)) {
104 _compRSet.add(*term) ;
105 } else if (dynamic_cast<RooAbsCategory*>(term)) {
106 _compCSet.add(*term) ;
107 } else {
108 coutE(InputArguments) << "RooProduct::addTerm(" << GetName() << ") ERROR: component " << term->GetName()
109 << " is not of type RooAbsReal or RooAbsCategory" << std::endl ;
110 throw std::invalid_argument("RooProduct can only handle terms deriving from RooAbsReal or RooAbsCategory.");
111 }
112}
113
114////////////////////////////////////////////////////////////////////////////////
115/// Force internal handling of integration of given observable if any
116/// of the product terms depend on it.
117
119{
120 // Force internal handling of integration of given observable if any
121 // of the product terms depend on it.
122
123 bool depends(false);
124 for (auto const* rcomp : static_range_cast<RooAbsReal*>(_compRSet)) {
125 if (depends) break;
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
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 RooArgList *indep = new RooArgList();
144 for (auto const* rcomp : static_range_cast<RooAbsReal*>(_compRSet)) {
145 if( !rcomp->dependsOn(allVars) ) indep->add(*rcomp);
146 }
147 if (!indep->empty()) {
148 map->push_back( std::make_pair(new RooArgSet(),indep) );
149 } else {
150 delete indep;
151 }
152
153 // Map observables -> functions ; start with individual observables
154 for (auto const* var : static_range_cast<RooAbsReal*>(allVars)) {
155 RooArgSet *vars = new RooArgSet(); vars->add(*var);
156 RooArgList *comps = new RooArgList();
157
158 for (auto const* rcomp2 : static_range_cast<RooAbsReal*>(_compRSet)) {
159 if( rcomp2->dependsOn(*var) ) comps->add(*rcomp2);
160 }
161 map->push_back( std::make_pair(vars,comps) );
162 }
163
164 // Merge groups with overlapping dependents
165 bool overlap;
166 do {
167 std::pair<ProdMap::iterator,ProdMap::iterator> i = findOverlap2nd(map->begin(),map->end());
168 overlap = (i.first!=i.second);
169 if (overlap) {
170 i.first->first->add(*i.second->first);
171
172 // In the merging step, make sure not to duplicate
173 for (auto const* targ : *(i.second->second)) {
174 if (!i.first->second->find(*targ)) {
175 i.first->second->add(*targ) ;
176 }
177 }
178 //i.first->second->add(*i.second->second);
179
180 delete i.second->first;
181 delete i.second->second;
182 map->erase(i.second);
183 }
184 } while (overlap);
185
186#ifndef NDEBUG
187 // check that we have all variables to be integrated over on the LHS
188 // of the map, and all terms in the product do appear on the RHS
189 std::size_t nVar=0;
190 std::size_t nFunc=0;
191 for (ProdMap::iterator i = map->begin();i!=map->end();++i) {
192 nVar+=i->first->size();
193 nFunc+=i->second->size();
194 }
195 assert(nVar==allVars.size());
197#endif
198 return map;
199}
200
201
202
203////////////////////////////////////////////////////////////////////////////////
204/// Return list of (partial) integrals whose product defines the integral of this
205/// RooProduct over the observables in iset in range isetRange. If no such list
206/// exists, create it now and store it in the cache for future use.
207
209{
210
211 // check if we already have integrals for this combination of factors
214 if (cache!=nullptr) {
215 Int_t code = _cacheMgr.lastIndex();
216 return code;
217 }
218
219 std::unique_ptr<ProdMap> map{groupProductTerms(*iset)};
220
221 cxcoutD(Integration) << "RooProduct::getPartIntList(" << GetName() << ") groupProductTerms returned map" ;
222 if (dologD(Integration)) {
223 dump_map(ccoutD(Integration),map->begin(),map->end());
224 ccoutD(Integration) << std::endl;
225 }
226
227 // did we find any factorizable terms?
228 if (map->size()<2) {
229
230 for (ProdMap::iterator iter = map->begin() ; iter != map->end() ; ++iter) {
231 delete iter->first ;
232 delete iter->second ;
233 }
234
235 return -1; // RRI caller will zero analVars if return code = 0....
236 }
237 cache = new CacheElem();
238
239 for (ProdMap::const_iterator i = map->begin();i!=map->end();++i) {
240 RooAbsReal *term(nullptr);
241 if (i->second->size()>1) { // create a RooProd for this subexpression
242 const char *name = makeFPName("SUBPROD_",*i->second);
243 auto ownedTerm = std::make_unique<RooProduct>(name,name,*i->second);
244 term = ownedTerm.get();
245 cache->_ownedList.addOwned(std::move(ownedTerm));
246 cxcoutD(Integration) << "RooProduct::getPartIntList(" << GetName() << ") created subexpression " << term->GetName() << std::endl;
247 } else {
248 assert(i->second->size()==1);
249 term = static_cast<RooAbsReal*>(i->second->at(0));
250 }
251 assert(term!=nullptr);
252 if (i->first->empty()) { // check whether we need to integrate over this term or not...
253 cache->_prodList.add(*term);
254 cxcoutD(Integration) << "RooProduct::getPartIntList(" << GetName() << ") adding simple factor " << term->GetName() << std::endl;
255 } else {
256 std::unique_ptr<RooAbsReal> integral{term->createIntegral(*i->first,isetRange)};
257 cache->_prodList.add(*integral);
258 cxcoutD(Integration) << "RooProduct::getPartIntList(" << GetName() << ") adding integral for " << term->GetName() << " : " << integral->GetName() << std::endl;
259 cache->_ownedList.addOwned(std::move(integral));
260 }
261 }
262 // add current set-up to cache, and return index..
264
265 cxcoutD(Integration) << "RooProduct::getPartIntList(" << GetName() << ") created list " << cache->_prodList << " with code " << code+1 << std::endl
266 << " for iset=" << *iset << " @" << iset << " range: " << (isetRange?isetRange:"<none>") << std::endl ;
267
268 for (ProdMap::iterator iter = map->begin() ; iter != map->end() ; ++iter) {
269 delete iter->first ;
270 delete iter->second ;
271 }
272 return code;
273}
274
275
276////////////////////////////////////////////////////////////////////////////////
277/// Declare that we handle all integrations internally
278
280 const RooArgSet* /*normSet*/,
281 const char* rangeName) const
282{
283 if (_forceNumInt) return 0 ;
284
285 // Declare that we can analytically integrate all requested observables
286 // (basically, we will take care of the problem, and delegate where required)
287 //assert(normSet==0);
288 assert(analVars.empty());
289 analVars.add(allVars) ;
291 return code ;
292}
293
294
295////////////////////////////////////////////////////////////////////////////////
296/// Calculate integral internally from appropriate partial integral cache
297
298double RooProduct::analyticalIntegral(Int_t code, const char* rangeName) const
299{
300 // note: rangeName implicit encoded in code: see _cacheMgr.setObj in getPartIntList...
301 CacheElem *cache = static_cast<CacheElem*>(_cacheMgr.getObjByIndex(code-1));
302 if (cache==nullptr) {
303 // cache got sterilized, trigger repopulation of this slot, then try again...
304 std::unique_ptr<RooArgSet> vars( getParameters(RooArgSet()) );
305 RooArgSet iset = _cacheMgr.selectFromSet2(*vars, code-1);
307 assert(code==code2); // must have revived the right (sterilized) slot...
309 }
310 assert(cache!=nullptr);
311
312 return calculate(cache->_prodList);
313}
314
315
316////////////////////////////////////////////////////////////////////////////////
317/// Calculate and return product of partial terms in partIntList
318
320{
321 double val=1;
322 for (const auto arg : partIntList) {
323 const auto term = static_cast<const RooAbsReal*>(arg);
324 double x = term->getVal();
325 val*= x;
326 }
327 return val;
328}
329
330
331////////////////////////////////////////////////////////////////////////////////
332/// Construct automatic name for internal product terms
333
334const char* RooProduct::makeFPName(const char *pfx,const RooArgSet& terms) const
335{
336 static TString pname;
337 pname = pfx;
338 bool first(true);
339 for (auto const* arg : terms) {
340 if (first) { first=false;}
341 else pname.Append("_X_");
342 pname.Append(arg->GetName());
343 }
344 return pname.Data();
345}
346
347
348
349////////////////////////////////////////////////////////////////////////////////
350/// Evaluate product of input functions
351
353{
354 double prod(1) ;
355
356 const RooArgSet* nset = _compRSet.nset() ;
357 for (const auto item : _compRSet) {
358 auto rcomp = static_cast<const RooAbsReal*>(item);
359
360 prod *= rcomp->getVal(nset) ;
361 }
362
363 for (const auto item : _compCSet) {
364 auto ccomp = static_cast<const RooAbsCategory*>(item);
365
366 prod *= ccomp->getCurrentIndex() ;
367 }
368
369 return prod ;
370}
371
372
374{
375 std::span<double> output = ctx.output();
376 std::size_t nEvents = output.size();
377
378 for (unsigned int i = 0; i < nEvents; ++i) {
379 output[i] = 1.;
380 }
381
382 for (const auto item : _compRSet) {
383 auto rcomp = static_cast<const RooAbsReal*>(item);
384 auto componentValues = ctx.at(rcomp);
385
386 for (unsigned int i = 0; i < nEvents; ++i) {
387 output[i] *= componentValues.size() == 1 ? componentValues[0] : componentValues[i];
388 }
389 }
390
391 for (const auto item : _compCSet) {
392 auto ccomp = static_cast<const RooAbsCategory*>(item);
393 const int catIndex = ccomp->getCurrentIndex();
394
395 for (unsigned int i = 0; i < nEvents; ++i) {
396 output[i] *= catIndex;
397 }
398 }
399}
400
401
402////////////////////////////////////////////////////////////////////////////////
403/// Forward the plot sampling hint from the p.d.f. that defines the observable obs
404
405std::list<double>* RooProduct::binBoundaries(RooAbsRealLValue& obs, double xlo, double xhi) const
406{
407 for (const auto item : _compRSet) {
408 auto func = static_cast<const RooAbsReal*>(item);
409
410 if (std::list<double>* binb = func->binBoundaries(obs,xlo,xhi)) {
411 return binb ;
412 }
413 }
414
415 return nullptr ;
416}
417
418
419//_____________________________________________________________________________B
421{
422 // If all components that depend on obs are binned that so is the product
423
424 for (const auto item : _compRSet) {
425 auto func = static_cast<const RooAbsReal*>(item);
426
427 if (func->dependsOn(obs) && !func->isBinnedDistribution(obs)) {
428 return false ;
429 }
430 }
431
432 return true ;
433}
434
435
436
437////////////////////////////////////////////////////////////////////////////////
438/// Forward the plot sampling hint from the p.d.f. that defines the observable obs
439
440std::list<double>* RooProduct::plotSamplingHint(RooAbsRealLValue& obs, double xlo, double xhi) const
441{
442 for (const auto item : _compRSet) {
443 auto func = static_cast<const RooAbsReal*>(item);
444
445 if (std::list<double>* hint = func->plotSamplingHint(obs,xlo,xhi)) {
446 return hint ;
447 }
448 }
449
450 return nullptr ;
451}
452
453
454
455////////////////////////////////////////////////////////////////////////////////
456/// Destructor
457
461
462
463////////////////////////////////////////////////////////////////////////////////
464/// Return list of all RooAbsArgs in cache element
465
467{
468 RooArgList ret(_ownedList) ;
469 return ret ;
470}
471
472
473
474
475////////////////////////////////////////////////////////////////////////////////
476/// Label OK'ed components of a RooProduct with cache-and-track
477
479{
481 for (const auto parg : comp) {
482 if (parg->isDerived()) {
483 if (parg->canNodeBeCached()==Always) {
484 trackNodes.add(*parg) ;
485 }
486 }
487 }
488}
489
490////////////////////////////////////////////////////////////////////////////////
491/// Customized printing of arguments of a RooProduct to more intuitively reflect the contents of the
492/// product operator construction
493
494void RooProduct::printMetaArgs(std::ostream& os) const
495{
496 bool first(true) ;
497
498 for (const auto rcomp : _compRSet) {
499 if (!first) { os << " * " ; } else { first = false ; }
500 os << rcomp->GetName() ;
501 }
502
503 for (const auto item : _compCSet) {
504 auto ccomp = static_cast<const RooAbsCategory*>(item);
505
506 if (!first) { os << " * " ; } else { first = false ; }
507 os << ccomp->GetName() ;
508 }
509
510 os << " " ;
511}
512
513
515 RooAbsReal::ioStreamerPass2(); // call the baseclass method
516
517 if(numProxies() < 2) {
518 throw std::runtime_error("RooProduct::ioStreamerPass2(): the number of proxies in the proxy list should be at least 2!");
519 }
520
521 // If the proxy data members are evolved by schema evolution, the proxy list
522 // that references them will contain null pointers because the evolved
523 // members are only created after the proxy list. That's why we have to set
524 // them manually in that case.
525 RooAbsProxy * p0 = getProxy(0);
526 if(p0 == nullptr) {
528 p0 = &_compRSet;
529 }
530 RooAbsProxy * p1 = getProxy(1);
531 if(p1 == nullptr) {
533 p1 = &_compCSet;
534 }
535
536 // If the proxies in the proxy list still don't correspond to _compRSet and
537 // _compCSet, it's time to print errors. And try to recover.
538 auto expectProxyIs = [this](std::size_t idx, RooAbsProxy * proxyInArg, RooListProxy * ourProxy, const char* memberName) {
539 if(proxyInArg != ourProxy) {
540 // From experience, it's rather the members of the RooProduct that is
541 // still correct in these inconsistent cases. That's why we try to
542 // recover by setting the proxy in the _proxyList to be equal to the
543 // member proxy. But that might be wrong, so it's important to warn the
544 // user anyway.
545 _proxyList.RemoveAt(idx);
547 std::stringstream ss;
548 ss << "Problem when reading RooProduct instance \"" << GetName() << "\"!\n"
549 << " _proxyList[" << idx << "] was expected to be equal to " << memberName << ", but it's not.\n"
550 << " - proxyList[" << idx << "] : ";
551 proxyInArg->print(ss, true);
552 ss << "\n - " << memberName << " : " ;
553 ourProxy->print(ss, true);
554 ss << "\n RooFit will resolve this inconsistency by making _proxyList[" << idx << "] point to " << memberName
555 << ".";
556 coutW(LinkStateMgmt) << ss.str() << std::endl;
557 }
558 };
559
560 expectProxyIs(0, p0, &_compRSet, "_compRSet");
561 expectProxyIs(1, p1, &_compCSet, "_compCSet");
562}
563
564
565namespace {
566
567std::pair<RPPMIter,RPPMIter> findOverlap2nd(RPPMIter i, RPPMIter end)
568{
569 // Utility function finding pairs of overlapping input functions
570 for (; i != end; ++i) {
571 for (RPPMIter j(i + 1); j != end; ++j) {
572 if (i->second->overlaps(*j->second)) {
573 return std::make_pair(i, j);
574 }
575 }
576 }
577 return std::make_pair(end,end);
578}
579
580
581void dump_map(std::ostream& os, RPPMIter i, RPPMIter end)
582{
583 // Utility dump function for debugging
584 bool first(true);
585 os << " [ " ;
586 for(; i!=end;++i) {
587 if (first) { first=false; }
588 else { os << " , " ; }
589 os << *(i->first) << " -> " << *(i->second) ;
590 }
591 os << " ] " ;
592}
593
594}
#define cxcoutD(a)
#define coutW(a)
#define dologD(a)
#define coutE(a)
#define ccoutD(a)
#define TRACE_DESTROY
Definition RooTrace.h:24
#define TRACE_CREATE
Definition RooTrace.h:23
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
char name[80]
Definition TGX11.cxx:110
Common abstract base class for objects that represent a value and a "shape" in RooFit.
Definition RooAbsArg.h:77
RooRefArray _proxyList
Definition RooAbsArg.h:578
RooFit::OwningPtr< RooArgSet > getParameters(const RooAbsData *data, bool stripDisconnected=true) const
Create a list of leaf nodes in the arg tree starting with ourself as top node that don't match any of...
virtual void ioStreamerPass2()
Method called by workspace container to finalize schema evolution issues that cannot be handled in a ...
Int_t numProxies() const
Return the number of registered proxies.
RooAbsProxy * getProxy(Int_t index) const
Return the nth proxy from the proxy list.
Abstract base class for objects to be stored in RooAbsCache cache manager objects.
A space to attach TBranches.
virtual bool add(const RooAbsArg &var, bool silent=false)
Add the specified argument to list.
Storage_t::size_type size() const
virtual bool addOwned(RooAbsArg &var, bool silent=false)
Add an argument and transfer the ownership to the collection.
Abstract interface for proxy classes.
Definition RooAbsProxy.h:37
const RooArgSet * nset() const
Definition RooAbsProxy.h:52
Abstract base class for objects that represent a real value that may appear on the left hand side of ...
Abstract base class for objects that represent a real value and implements functionality common to al...
Definition RooAbsReal.h:59
bool _forceNumInt
Force numerical integration if flag set.
Definition RooAbsReal.h:538
RooArgList is a container object that can hold multiple RooAbsArg objects.
Definition RooArgList.h:22
RooArgSet is a container object that can hold multiple RooAbsArg objects.
Definition RooArgSet.h:24
Int_t setObj(const RooArgSet *nset, T *obj, const TNamed *isetRangeName=nullptr)
Setter function without integration set.
T * getObjByIndex(Int_t index) const
Retrieve payload object by slot index.
RooArgSet selectFromSet2(RooArgSet const &argSet, int index) const
Create RooArgSet containing the objects that are both in the cached set 2 with a given index and an i...
Int_t lastIndex() const
Return index of slot used in last get or set operation.
T * getObj(const RooArgSet *nset, Int_t *sterileIndex=nullptr, const TNamed *isetRangeName=nullptr)
Getter function without integration set.
bool add(const RooAbsArg &var, bool valueServer, bool shapeServer, bool silent)
Overloaded RooCollection_t::add() method insert object into set and registers object as server to own...
std::span< const double > at(RooAbsArg const *arg, RooAbsArg const *caller=nullptr)
std::span< double > output()
static const TNamed * ptr(const char *stringPtr)
Return a unique TNamed pointer for given C++ string.
RooArgList containedArgs(Action) override
Return list of all RooAbsArgs in cache element.
RooArgList _ownedList
Definition RooProduct.h:78
~CacheElem() override
Destructor.
Represents the product of a given set of RooAbsReal objects.
Definition RooProduct.h:29
std::list< double > * binBoundaries(RooAbsRealLValue &, double, double) const override
Forward the plot sampling hint from the p.d.f. that defines the observable obs.
void doEval(RooFit::EvalContext &) const override
Base function for computing multiple values of a RooAbsReal.
RooListProxy _compRSet
Definition RooProduct.h:70
double evaluate() const override
Evaluate product of input functions.
double analyticalIntegral(Int_t code, const char *rangeName=nullptr) const override
Calculate integral internally from appropriate partial integral cache.
void addTerm(RooAbsArg *term)
Add a term to this product.
std::list< double > * plotSamplingHint(RooAbsRealLValue &, double, double) const override
Forward the plot sampling hint from the p.d.f. that defines the observable obs.
ProdMap * groupProductTerms(const RooArgSet &) const
Group observables into subsets in which the product factorizes and that can thus be integrated separa...
double calculate(const RooArgList &partIntList) const
The cache manager.
bool forceAnalyticalInt(const RooAbsArg &dep) const override
Force internal handling of integration of given observable if any of the product terms depend on it.
void setCacheAndTrackHints(RooArgSet &) override
Label OK'ed components of a RooProduct with cache-and-track.
~RooProduct() override
Destructor.
bool isBinnedDistribution(const RooArgSet &obs) const override
Tests if the distribution is binned. Unless overridden by derived classes, this always returns false.
void ioStreamerPass2() override
Method called by workspace container to finalize schema evolution issues that cannot be handled in a ...
Int_t getPartIntList(const RooArgSet *iset, const char *rangeName=nullptr) const
Return list of (partial) integrals whose product defines the integral of this RooProduct over the obs...
Int_t getAnalyticalIntegralWN(RooArgSet &allVars, RooArgSet &analVars, const RooArgSet *normSet, const char *rangeName=nullptr) const override
Declare that we handle all integrations internally.
void printMetaArgs(std::ostream &os) const override
Customized printing of arguments of a RooProduct to more intuitively reflect the contents of the prod...
const char * makeFPName(const char *pfx, const RooArgSet &terms) const
Construct automatic name for internal product terms.
RooProduct()
Default constructor.
RooObjCacheManager _cacheMgr
Definition RooProduct.h:81
RooListProxy _compCSet
Definition RooProduct.h:71
RooArgList components()
Definition RooProduct.h:48
const char * GetName() const override
Returns name of object.
Definition TNamed.h:49
void AddAt(TObject *obj, Int_t idx) override
Add object at position ids.
TObject * RemoveAt(Int_t idx) override
Remove object at index idx.
Basic string class.
Definition TString.h:139
Double_t x[n]
Definition legend1.C:17
static void output()