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