Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RooProdPdf.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\file RooProdPdf.cxx
19\class RooProdPdf
20\ingroup Roofitcore
21
22Efficient implementation of a product of PDFs of the form
23\f[ \prod_{i=1}^{N} \mathrm{PDF}_i (x, \ldots) \f]
24
25PDFs may share observables. If that is the case any irreducible subset
26of PDFs that share observables will be normalised with explicit numeric
27integration as any built-in normalisation will no longer be valid.
28
29Alternatively, products using conditional PDFs can be defined, *e.g.*
30
31\f[ F(x|y) \cdot G(y), \f]
32
33meaning a PDF \f$ F(x) \f$ **given** \f$ y \f$ and a PDF \f$ G(y) \f$.
34In this construction, \f$ F \f$ is only
35normalised w.r.t \f$ x\f$, and \f$ G \f$ is normalised w.r.t \f$ y \f$. The product in this construction
36is properly normalised.
37
38If exactly one of the component PDFs supports extended likelihood fits, the
39product will also be usable in extended mode, returning the number of expected
40events from the extendable component PDF. The extendable component does not
41have to appear in any specific place in the list.
42**/
43
44#include "RooProdPdf.h"
45#include "RooBatchCompute.h"
46#include "RooRealProxy.h"
47#include "RooProdGenContext.h"
48#include "RooGenProdProj.h"
49#include "RooProduct.h"
50#include "RooNameReg.h"
51#include "RooMsgService.h"
52#include "RooFormulaVar.h"
53#include "RooRealVar.h"
54#include "RooAddition.h"
55#include "RooGlobalFunc.h"
56#include "RooConstVar.h"
57#include "RooWorkspace.h"
58#include "RooRangeBoolean.h"
59#include "RooCustomizer.h"
60#include "RooRealIntegral.h"
61#include "RooFitImplHelpers.h"
63#include "strtok.h"
64
65#include <ROOT/StringUtils.hxx>
66
67#include <algorithm>
68#include <array>
69#include <cstring>
70#include <sstream>
71#include <string>
72#include <unordered_map>
73#include <unordered_set>
74
75#ifndef _WIN32
76#include <strings.h>
77#endif
78
79using std::endl, std::string, std::vector, std::list, std::ostream, std::map, std::ostringstream;
80
81
82
83////////////////////////////////////////////////////////////////////////////////
84/// Default constructor
85
87 _cacheMgr(this,10)
88{
89 // Default constructor
90}
91
92
93////////////////////////////////////////////////////////////////////////////////
94/// Constructor with 2 PDFs (most frequent use case).
95///
96/// The optional cutOff parameter can be used as a speed optimization if
97/// one or more of the PDF have sizable regions with very small values,
98/// which would pull the entire product of PDFs to zero in those regions.
99///
100/// After each PDF multiplication, the running product is compared with
101/// the cutOff parameter. If the running product is smaller than the
102/// cutOff value, the product series is terminated and remaining PDFs
103/// are not evaluated.
104///
105/// There is no magic value of the cutOff, the user should experiment
106/// to find the appropriate balance between speed and precision.
107/// If a cutoff is specified, the PDFs most likely to be small should
108/// be put first in the product. The default cutOff value is zero.
109///
110
111RooProdPdf::RooProdPdf(const char *name, const char *title,
112 RooAbsPdf& pdf1, RooAbsPdf& pdf2, double cutOff) :
113 RooAbsPdf(name,title),
114 _cacheMgr(this,10),
115 _cutOff(cutOff),
116 _pdfList("!pdfs","List of PDFs",this)
117{
118 _pdfList.add(pdf1) ;
119 _pdfNSetList.emplace_back(std::make_unique<RooArgSet>("nset")) ;
120 if (pdf1.canBeExtended()) {
121 _extendedIndex = _pdfList.index(&pdf1) ;
122 }
123
124 _pdfList.add(pdf2) ;
125 _pdfNSetList.emplace_back(std::make_unique<RooArgSet>("nset")) ;
126
127 if (pdf2.canBeExtended()) {
128 if (_extendedIndex>=0) {
129 // Protect against multiple extended terms
130 coutW(InputArguments) << "RooProdPdf::RooProdPdf(" << GetName()
131 << ") multiple components with extended terms detected,"
132 << " product will not be extendable." << std::endl ;
133 _extendedIndex=-1 ;
134 } else {
136 }
137 }
138}
139
140
141
142////////////////////////////////////////////////////////////////////////////////
143/// Constructor from a list of PDFs.
144///
145/// The optional cutOff parameter can be used as a speed optimization if
146/// one or more of the PDF have sizable regions with very small values,
147/// which would pull the entire product of PDFs to zero in those regions.
148///
149/// After each PDF multiplication, the running product is compared with
150/// the cutOff parameter. If the running product is smaller than the
151/// cutOff value, the product series is terminated and remaining PDFs
152/// are not evaluated.
153///
154/// There is no magic value of the cutOff, the user should experiment
155/// to find the appropriate balance between speed and precision.
156/// If a cutoff is specified, the PDFs most likely to be small should
157/// be put first in the product. The default cutOff value is zero.
158
159RooProdPdf::RooProdPdf(const char* name, const char* title, const RooArgList& inPdfList, double cutOff) :
160 RooAbsPdf(name,title),
161 _cacheMgr(this,10),
162 _cutOff(cutOff),
163 _pdfList("!pdfs","List of PDFs",this)
164{
165 addPdfs(inPdfList);
166}
167
168
169
170////////////////////////////////////////////////////////////////////////////////
171/// Constructor from named argument list.
172/// \param[in] name Name used by RooFit
173/// \param[in] title Title used for plotting
174/// \param[in] fullPdfSet Set of "regular" PDFs that are normalised over all their observables
175/// \param[in] arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8 Optional arguments according to table below.
176///
177/// <table>
178/// <tr><th> Argument <th> Description
179/// <tr><td> `Conditional(pdfSet,depSet,depsAreCond=false)` <td> Add PDF to product with condition that it
180/// only be normalized over specified observables. Any remaining observables will be conditional observables.
181/// (Setting `depsAreCond` to true inverts this, so the observables in depSet will be the conditional observables.)
182/// </table>
183///
184/// For example, given a PDF \f$ F(x,y) \f$ and \f$ G(y) \f$,
185///
186/// `RooProdPdf("P", "P", G, Conditional(F,x))` will construct a 2-dimensional PDF as follows:
187/// \f[
188/// P(x,y) = \frac{G(y)}{\int_y G(y)} \cdot \frac{F(x,y)}{\int_x F(x,y)},
189/// \f]
190///
191/// which is a well normalised and properly defined PDF, but different from
192/// \f[
193/// P'(x,y) = \frac{F(x,y) \cdot G(y)}{\int_x\int_y F(x,y) \cdot G(y)}.
194/// \f]
195///
196/// In the former case, the \f$ y \f$ distribution of \f$ P \f$ is identical to that of \f$ G \f$, while
197/// \f$ F \f$ only is used to determine the correlation between \f$ X \f$ and \f$ Y \f$. In the latter
198/// case, the \f$ Y \f$ distribution is defined by the product of \f$ F \f$ and \f$ G \f$.
199///
200/// This \f$ P(x,y) \f$ construction is analogous to generating events from \f$ F(x,y) \f$ with
201/// a prototype dataset sampled from \f$ G(y) \f$.
202
203RooProdPdf::RooProdPdf(const char* name, const char* title, const RooArgSet& fullPdfSet,
204 const RooCmdArg& arg1, const RooCmdArg& arg2,
205 const RooCmdArg& arg3, const RooCmdArg& arg4,
206 const RooCmdArg& arg5, const RooCmdArg& arg6,
207 const RooCmdArg& arg7, const RooCmdArg& arg8) :
208 RooAbsPdf(name,title),
209 _cacheMgr(this,10),
210 _pdfList("!pdfs","List of PDFs",this)
211{
213 l.Add((TObject*)&arg1) ; l.Add((TObject*)&arg2) ;
214 l.Add((TObject*)&arg3) ; l.Add((TObject*)&arg4) ;
215 l.Add((TObject*)&arg5) ; l.Add((TObject*)&arg6) ;
216 l.Add((TObject*)&arg7) ; l.Add((TObject*)&arg8) ;
217
218 initializeFromCmdArgList(fullPdfSet,l) ;
219}
220
221
222
223////////////////////////////////////////////////////////////////////////////////
224/// Constructor from named argument list
225
226RooProdPdf::RooProdPdf(const char* name, const char* title,
227 const RooCmdArg& arg1, const RooCmdArg& arg2,
228 const RooCmdArg& arg3, const RooCmdArg& arg4,
229 const RooCmdArg& arg5, const RooCmdArg& arg6,
230 const RooCmdArg& arg7, const RooCmdArg& arg8) :
231 RooAbsPdf(name,title),
232 _cacheMgr(this,10),
233 _pdfList("!pdfList","List of PDFs",this)
234{
236 l.Add((TObject*)&arg1) ; l.Add((TObject*)&arg2) ;
237 l.Add((TObject*)&arg3) ; l.Add((TObject*)&arg4) ;
238 l.Add((TObject*)&arg5) ; l.Add((TObject*)&arg6) ;
239 l.Add((TObject*)&arg7) ; l.Add((TObject*)&arg8) ;
240
242}
243
244
245
246////////////////////////////////////////////////////////////////////////////////
247/// Internal constructor from list of named arguments
248
249RooProdPdf::RooProdPdf(const char* name, const char* title, const RooArgSet& fullPdfSet, const RooLinkedList& cmdArgList) :
250 RooAbsPdf(name,title),
251 _cacheMgr(this,10),
252 _pdfList("!pdfs","List of PDFs",this)
253{
254 initializeFromCmdArgList(fullPdfSet, cmdArgList) ;
255}
256
257
258
259////////////////////////////////////////////////////////////////////////////////
260/// Copy constructor
261
262RooProdPdf::RooProdPdf(const RooProdPdf& other, const char* name) :
263 RooAbsPdf(other,name),
264 _cacheMgr(other._cacheMgr,this),
265 _genCode(other._genCode),
266 _cutOff(other._cutOff),
267 _pdfList("!pdfs",this,other._pdfList),
268 _extendedIndex(other._extendedIndex),
269 _useDefaultGen(other._useDefaultGen),
270 _refRangeName(other._refRangeName),
271 _selfNorm(other._selfNorm),
272 _defNormSet(other._defNormSet)
273{
274 // Clone contents of normalizarion set list
275 for(auto const& nset : other._pdfNSetList) {
276 _pdfNSetList.emplace_back(std::make_unique<RooArgSet>(nset->GetName()));
277 nset->snapshot(*_pdfNSetList.back());
278 }
279}
280
281
282
283////////////////////////////////////////////////////////////////////////////////
284/// Initialize RooProdPdf configuration from given list of RooCmdArg configuration arguments
285/// and set of 'regular' p.d.f.s in product
286
288{
289 Int_t numExtended(0) ;
290
291 // Process set of full PDFS
292 for(auto const* pdf : static_range_cast<RooAbsPdf*>(fullPdfSet)) {
293 _pdfList.add(*pdf) ;
294 _pdfNSetList.emplace_back(std::make_unique<RooArgSet>("nset")) ;
295
296 if (pdf->canBeExtended()) {
298 numExtended++ ;
299 }
300
301 }
302
303 // Process list of conditional PDFs
304 for(auto * carg : static_range_cast<RooCmdArg*>(l)) {
305
306 if (0 == strcmp(carg->GetName(), "Conditional")) {
307
308 Int_t argType = carg->getInt(0) ;
309 auto pdfSet = static_cast<RooArgSet const*>(carg->getSet(0));
310 auto normSet = static_cast<RooArgSet const*>(carg->getSet(1));
311
312 for(auto * thePdf : static_range_cast<RooAbsPdf*>(*pdfSet)) {
313 _pdfList.add(*thePdf) ;
314
315 _pdfNSetList.emplace_back(std::make_unique<RooArgSet>(0 == argType ? "nset" : "cset"));
316 normSet->snapshot(*_pdfNSetList.back());
317
318 if (thePdf->canBeExtended()) {
319 _extendedIndex = _pdfList.index(thePdf) ;
320 numExtended++ ;
321 }
322
323 }
324
325 } else if (0 != strlen(carg->GetName())) {
326 coutW(InputArguments) << "Unknown arg: " << carg->GetName() << std::endl ;
327 }
328 }
329
330 // Protect against multiple extended terms
331 if (numExtended>1) {
332 coutW(InputArguments) << "RooProdPdf::RooProdPdf(" << GetName()
333 << ") WARNING: multiple components with extended terms detected,"
334 << " product will not be extendable." << std::endl ;
335 _extendedIndex = -1 ;
336 }
337
338
339}
340
341
342
343////////////////////////////////////////////////////////////////////////////////
344/// Destructor
345
349
350
352 int code ;
353 auto cache = static_cast<CacheElem*>(_cacheMgr.getObj(nset, nullptr, &code)) ;
354
355 // If cache doesn't have our configuration, recalculate here
356 if (!cache) {
357 code = getPartIntList(nset, nullptr) ;
358 cache = static_cast<CacheElem*>(_cacheMgr.getObj(nset, nullptr, &code)) ;
359 }
360 return cache;
361}
362
363
364////////////////////////////////////////////////////////////////////////////////
365/// Calculate current value of object
366
368{
370}
371
372
373
374////////////////////////////////////////////////////////////////////////////////
375/// Calculate running product of pdfs terms, using the supplied
376/// normalization set in 'normSetList' for each component
377
378double RooProdPdf::calculate(const RooProdPdf::CacheElem& cache, bool /*verbose*/) const
379{
380 if (cache._isRearranged) {
381 if (dologD(Eval)) {
382 cxcoutD(Eval) << "RooProdPdf::calculate(" << GetName() << ") rearranged product calculation"
383 << " calculate: num = " << cache._rearrangedNum->GetName() << " = " << cache._rearrangedNum->getVal() << std::endl ;
384// cache._rearrangedNum->printComponentTree("",0,5) ;
385 cxcoutD(Eval) << "calculate: den = " << cache._rearrangedDen->GetName() << " = " << cache._rearrangedDen->getVal() << std::endl ;
386// cache._rearrangedDen->printComponentTree("",0,5) ;
387 }
388
389 return cache._rearrangedNum->getVal() / cache._rearrangedDen->getVal();
390 } else {
391
392 double value = 1.0;
393 assert(cache._normList.size() == cache._partList.size());
394 for (std::size_t i = 0; i < cache._partList.size(); ++i) {
395 const auto& partInt = static_cast<const RooAbsReal&>(cache._partList[i]);
396 const auto normSet = cache._normList[i].get();
397
398 const double piVal = partInt.getVal(!normSet->empty() ? normSet : nullptr);
399 value *= piVal ;
400 if (value <= _cutOff) break;
401 }
402
403 return value ;
404 }
405}
406
407namespace {
408
409template<class T>
410void eraseNullptrs(std::vector<T*>& v) {
411 v.erase(std::remove_if(v.begin(), v.end(), [](T* x){ return x == nullptr; } ), v.end());
412}
413
414void removeCommon(std::vector<RooAbsArg*> &v, std::span<RooAbsArg * const> other) {
415
416 for (auto const& arg : other) {
417 auto namePtrMatch = [&arg](const RooAbsArg* elm) {
418 return elm != nullptr && elm->namePtr() == arg->namePtr();
419 };
420
421 auto found = std::find_if(v.begin(), v.end(), namePtrMatch);
422 if(found != v.end()) {
423 *found = nullptr;
424 }
425 }
426 eraseNullptrs(v);
427}
428
429void addCommon(std::vector<RooAbsArg*> &v, std::vector<RooAbsArg*> const& o1, std::vector<RooAbsArg*> const& o2) {
430
431 for (auto const& arg : o1) {
432 auto namePtrMatch = [&arg](const RooAbsArg* elm) {
433 return elm->namePtr() == arg->namePtr();
434 };
435
436 if(std::find_if(o2.begin(), o2.end(), namePtrMatch) != o2.end()) {
437 v.push_back(arg);
438 }
439 }
440}
441
442bool isRangeIdentical(RooArgSet const &observables, TString const &normRange, TNamed *refRangeName)
443{
444 // FK: Here the refRange should be compared to normRange, if it's set, and to the normObs range if it's not set
445 const char *range = normRange.Length() > 0 ? normRange.Data() : nullptr;
446 const char *refRange = RooNameReg::str(refRangeName);
447 for (auto const *normObs : static_range_cast<RooRealVar *>(observables)) {
448 if (normObs->getMin(range) != normObs->getMin(refRange) || normObs->getMax(range) != normObs->getMax(refRange))
449 return false;
450 }
451 return true;
452}
453
454}
455
456
457////////////////////////////////////////////////////////////////////////////////
458/// Factorize product in irreducible terms for given choice of integration/normalization
459
460void RooProdPdf::factorizeProduct(const RooArgSet& normSet, const RooArgSet& intSet, Factorized &factorized) const
461{
462 // List of all term dependents: normalization and imported
463 std::vector<RooArgSet> depAllList;
464 std::vector<RooArgSet> depIntNoNormList;
465
466 // Setup lists for factorization terms and their dependents
467 RooArgSet* term(nullptr);
468 RooArgSet* termIntDeps(nullptr);
469 RooArgSet* termIntNoNormDeps(nullptr);
470
471 std::vector<RooAbsArg*> pdfIntNoNormDeps;
472 std::vector<RooAbsArg*> pdfIntSet;
473 std::vector<RooAbsArg*> pdfNSet;
474 std::vector<RooAbsArg*> pdfCSet;
475 std::vector<RooAbsArg*> pdfNormDeps; // Dependents to be normalized for the PDF
476 std::vector<RooAbsArg*> pdfAllDeps; // All dependents of this PDF
477
478 // Loop over the PDFs
479 for(std::size_t iPdf = 0; iPdf < _pdfList.size(); ++iPdf) {
480 RooAbsPdf& pdf = static_cast<RooAbsPdf&>(_pdfList[iPdf]);
481 RooArgSet& pdfNSetOrig = *_pdfNSetList[iPdf];
482
483 pdfNSet.clear();
484 pdfCSet.clear();
485
486 // Make iterator over tree leaf node list to get the observables.
487 // This code is borrowed from RooAbsPdf::getObservables().
488 // RooAbsArg::treeNodeServer list is relatively expensive, so we only do it
489 // once and use it in a lambda function.
490 RooArgSet pdfLeafList("leafNodeServerList") ;
491 pdf.treeNodeServerList(&pdfLeafList,nullptr,false,true,true) ;
492 auto getObservablesOfCurrentPdf = [&pdfLeafList](
493 std::vector<RooAbsArg*> & out,
494 const RooArgSet& dataList) {
495 for (const auto arg : pdfLeafList) {
496 if (arg->dependsOnValue(dataList) && arg->isLValue()) {
497 out.push_back(arg) ;
498 }
499 }
500 };
501
502 // Reduce pdfNSet to actual dependents
503 if (0 == strcmp("cset", pdfNSetOrig.GetName())) {
504 getObservablesOfCurrentPdf(pdfNSet, normSet);
505 removeCommon(pdfNSet, pdfNSetOrig.get());
506 pdfCSet = pdfNSetOrig.get();
507 } else {
508 // Interpret at NSet
509 getObservablesOfCurrentPdf(pdfNSet, pdfNSetOrig);
510 }
511
512
513 pdfNormDeps.clear();
514 pdfAllDeps.clear();
515
516 // Make list of all dependents of this PDF
517 getObservablesOfCurrentPdf(pdfAllDeps, normSet);
518
519
520 // Make list of normalization dependents for this PDF;
521 if (!pdfNSet.empty()) {
522 // PDF is conditional
523 addCommon(pdfNormDeps, pdfAllDeps, pdfNSet);
524 } else {
525 // PDF is regular
526 pdfNormDeps = pdfAllDeps;
527 }
528
529 pdfIntSet.clear();
530 getObservablesOfCurrentPdf(pdfIntSet, intSet) ;
531
532 // WVE if we have no norm deps, conditional observables should be taken out of pdfIntSet
533 if (pdfNormDeps.empty() && !pdfCSet.empty()) {
534 removeCommon(pdfIntSet, pdfCSet);
535 }
536
537 pdfIntNoNormDeps.clear();
538 pdfIntNoNormDeps = pdfIntSet;
539 removeCommon(pdfIntNoNormDeps, pdfNormDeps);
540
541 // Check if this PDF has dependents overlapping with one of the existing terms
542 bool done = false;
543 int j = 0;
544 auto lIter = factorized.terms.begin();
545 auto ldIter = factorized.norms.begin();
546 for(;lIter != factorized.terms.end(); (++lIter, ++ldIter, ++j)) {
547 RooArgSet *termNormDeps = static_cast<RooArgSet*>(*ldIter);
548 term = static_cast<RooArgSet*>(*lIter);
549 // PDF should be added to existing term if
550 // 1) It has overlapping normalization dependents with any other PDF in existing term
551 // 2) It has overlapping dependents of any class for which integration is requested
552 // 3) If normalization happens over multiple ranges, and those ranges are both defined
553 // in either observable
554
555 bool normOverlap = termNormDeps->overlaps(pdfNormDeps.begin(), pdfNormDeps.end());
556 //bool intOverlap = pdfIntSet->overlaps(*termAllDeps);
557
558 if (normOverlap) {
559 term->add(pdf);
560 termNormDeps->add(pdfNormDeps.begin(), pdfNormDeps.end(), false);
561 depAllList[j].add(pdfAllDeps.begin(), pdfAllDeps.end(), false);
562 if (termIntDeps) {
563 termIntDeps->add(pdfIntSet.begin(), pdfIntSet.end(), false);
564 }
565 if (termIntNoNormDeps) {
566 termIntNoNormDeps->add(pdfIntNoNormDeps.begin(), pdfIntNoNormDeps.end(), false);
567 }
568 termIntNoNormDeps->add(pdfIntNoNormDeps.begin(), pdfIntNoNormDeps.end(), false);
569 done = true;
570 }
571 }
572
573 // If not, create a new term
574 if (!done) {
575 if (!(pdfNormDeps.empty() && pdfAllDeps.empty() &&
576 pdfIntSet.empty()) || normSet.empty()) {
577 term = new RooArgSet("term");
578 RooArgSet *termNormDeps = new RooArgSet("termNormDeps");
579 depAllList.emplace_back(pdfAllDeps.begin(), pdfAllDeps.end(), "termAllDeps");
580 termIntDeps = new RooArgSet(pdfIntSet.begin(), pdfIntSet.end(), "termIntDeps");
581 depIntNoNormList.emplace_back(pdfIntNoNormDeps.begin(), pdfIntNoNormDeps.end(), "termIntNoNormDeps");
582 termIntNoNormDeps = &depIntNoNormList.back();
583
584 term->add(pdf);
585 termNormDeps->add(pdfNormDeps.begin(), pdfNormDeps.end(), false);
586
587 factorized.terms.Add(term);
588 factorized.norms.Add(termNormDeps);
589 factorized.ints.Add(termIntDeps);
590 }
591 }
592
593 }
594
595 // Loop over list of terms again to determine 'imported' observables
596 int i = 0;
597 RooArgSet *normDeps;
598 auto lIter = factorized.terms.begin();
599 auto ldIter = factorized.norms.begin();
600 for(;lIter != factorized.terms.end(); (++lIter, ++ldIter, ++i)) {
601 normDeps = static_cast<RooArgSet*>(*ldIter);
602 term = static_cast<RooArgSet*>(*lIter);
603 // Make list of wholly imported dependents
604 RooArgSet impDeps(depAllList[i]);
605 impDeps.remove(*normDeps, true, true);
606 auto snap = new RooArgSet;
607 impDeps.snapshot(*snap);
608 factorized.imps.Add(snap);
609
610 // Make list of cross dependents (term is self contained for these dependents,
611 // but components import dependents from other components)
612 auto crossDeps = std::unique_ptr<RooAbsCollection>{depIntNoNormList[i].selectCommon(*normDeps)};
613 snap = new RooArgSet;
614 crossDeps->snapshot(*snap);
615 factorized.cross.Add(snap);
616 }
617}
618
619
620
621
622////////////////////////////////////////////////////////////////////////////////
623/// Return list of (partial) integrals of product terms for integration
624/// of p.d.f over observables iset while normalization over observables nset.
625/// Also return list of normalization sets to be used to evaluate
626/// each component in the list correctly.
627
628Int_t RooProdPdf::getPartIntList(const RooArgSet* nset, const RooArgSet* iset, const char* isetRangeName) const
629{
630 // Check if this configuration was created before
631 Int_t sterileIdx(-1);
632
633 if (static_cast<CacheElem*>(_cacheMgr.getObj(nset,iset,&sterileIdx,isetRangeName))) {
634 return _cacheMgr.lastIndex();
635 }
636
637 std::unique_ptr<CacheElem> cache = createCacheElem(nset, iset, isetRangeName);
638
639 // Store the partial integral list and return the assigned code
640 return _cacheMgr.setObj(nset, iset, cache.release(), RooNameReg::ptr(isetRangeName));
641}
642
643
644
645std::unique_ptr<RooProdPdf::CacheElem> RooProdPdf::createCacheElem(const RooArgSet* nset,
646 const RooArgSet* iset,
647 const char* isetRangeName) const
648{
649 // Create containers for partial integral components to be generated
650 auto cache = std::make_unique<CacheElem>();
651
652 // Factorize the product in irreducible terms for this nset
653 Factorized factorized;
654
655 // Normalization set used for factorization
656 RooArgSet factNset(nset ? (*nset) : _defNormSet);
657
658 factorizeProduct(factNset, iset ? (*iset) : RooArgSet(), factorized);
659
660 // Group irriducible terms that need to be (partially) integrated together
661 std::list<std::vector<RooArgSet*>> groupedList;
662 RooArgSet outerIntDeps;
663 groupProductTerms(groupedList, outerIntDeps, factorized);
664
665 // Loop over groups
666 // Find groups of type F(x|y), i.e. termImpSet!=0, construct ratio object
667 std::map<std::string, RooArgSet> ratioTerms;
668 for (auto const& group : groupedList) {
669 if (1 == group.size()) {
670 RooArgSet* term = group[0];
671
672 Int_t termIdx = factorized.terms.IndexOf(term);
673 RooArgSet *norm=static_cast<RooArgSet*>(factorized.norms.At(termIdx));
674 RooArgSet *imps=static_cast<RooArgSet*>(factorized.imps.At(termIdx));
675 RooArgSet termNSet(*norm);
676 RooArgSet termImpSet(*imps);
677
678 if (!termImpSet.empty() && nullptr != _refRangeName) {
679
680 // WVE we can skip this if the ref range is equal to the normalization range
681 // LM : avoid making integral ratio if range is the same. Why was not included ??? (same at line 857)
682 if (!isRangeIdentical(termNSet, _normRange, _refRangeName)) {
683 auto ratio = makeCondPdfRatioCorr(*static_cast<RooAbsReal*>(term->first()), termNSet, termImpSet, normRange(), RooNameReg::str(_refRangeName));
684 std::ostringstream str; termImpSet.printValue(str);
685 ratioTerms[str.str()].addOwned(std::move(ratio));
686 }
687 }
688
689 } else {
690 for (auto const& term : group) {
691
692 Int_t termIdx = factorized.terms.IndexOf(term);
693 RooArgSet *norm=static_cast<RooArgSet*>(factorized.norms.At(termIdx));
694 RooArgSet *imps=static_cast<RooArgSet*>(factorized.imps.At(termIdx));
695 RooArgSet termNSet(*norm);
696 RooArgSet termImpSet(*imps);
697
698 if (!termImpSet.empty() && nullptr != _refRangeName) {
699
700 // WVE we can skip this if the ref range is equal to the normalization range
701 if (!isRangeIdentical(termNSet, _normRange, _refRangeName)) {
702 auto ratio = makeCondPdfRatioCorr(*static_cast<RooAbsReal*>(term->first()), termNSet, termImpSet, normRange(), RooNameReg::str(_refRangeName));
703 std::ostringstream str; termImpSet.printValue(str);
704 ratioTerms[str.str()].addOwned(std::move(ratio));
705 }
706 }
707 }
708 }
709
710 }
711
712 // Find groups with y as termNSet
713 // Replace G(y) with (G(y),ratio)
714 for (auto const& group : groupedList) {
715 for (auto const& term : group) {
716 Int_t termIdx = factorized.terms.IndexOf(term);
717 RooArgSet *norm = static_cast<RooArgSet*>(factorized.norms.At(termIdx));
718 RooArgSet *imps = static_cast<RooArgSet*>(factorized.imps.At(termIdx));
719 RooArgSet termNSet(*norm);
720 RooArgSet termImpSet(*imps);
721
722 // If termNset matches index of ratioTerms, insert ratio here
723 ostringstream str; termNSet.printValue(str);
724 if (!ratioTerms[str.str()].empty()) {
725 term->add(ratioTerms[str.str()]);
726 cache->_ownedList.addOwned(std::move(ratioTerms[str.str()]));
727 }
728 }
729 }
730
731 for (auto const& group : groupedList) {
732 if (1 == group.size()) {
733 RooArgSet* term = group[0];
734
735 Int_t termIdx = factorized.terms.IndexOf(term);
736 RooArgSet *norm = factorized.termNormDeps(termIdx);
737 RooArgSet *integ = factorized.termIntDeps(termIdx);
738 RooArgSet *xdeps = factorized.termCrossDeps(termIdx);
739 RooArgSet *imps = factorized.termImpDeps(termIdx);
740
741 // Take list of normalization, integrated dependents, and
742 // cross-imported integrated dependents from factorization algorithm
743 RooArgSet termNSet{*norm};
744 RooArgSet termISet{*integ};
745 RooArgSet termXSet{*xdeps};
746 RooArgSet termImpSet{*imps};
747
748 // Add prefab term to partIntList.
749 auto func = processProductTerm(nset, iset, isetRangeName, term, termNSet, termISet);
750 if (func.x0) {
751 cache->_partList.add(*func.x0);
752 if (func.isOwned) cache->_ownedList.addOwned(std::unique_ptr<RooAbsArg>{func.x0});
753
754 cache->_normList.emplace_back(std::make_unique<RooArgSet>());
755 norm->snapshot(*cache->_normList.back(), false);
756
757 cache->_numList.addOwned(std::move(func.x1));
758 cache->_denList.addOwned(std::move(func.x2));
759 }
760 } else {
761 RooArgSet compTermSet;
762 RooArgSet compTermNorm;
763 RooArgSet compTermNum;
764 RooArgSet compTermDen;
765 for (auto const &term : group) {
766 Int_t termIdx = factorized.terms.IndexOf(term);
767 RooArgSet *norm = factorized.termNormDeps(termIdx);
768 RooArgSet *integ = factorized.termIntDeps(termIdx);
769 RooArgSet *xdeps = factorized.termCrossDeps(termIdx);
770 RooArgSet *imps = factorized.termImpDeps(termIdx);
771
772 RooArgSet termNSet{*norm};
773 RooArgSet termISet{*integ};
774 RooArgSet termXSet{*xdeps};
775 RooArgSet termImpSet{*imps};
776
777 // Remove outer integration dependents from termISet
778 termISet.remove(outerIntDeps, true, true);
779
780 auto func = processProductTerm(nset, iset, isetRangeName, term, termNSet, termISet, true);
781 if (func.x0) {
782 compTermSet.add(*func.x0);
783 if (func.isOwned) cache->_ownedList.addOwned(std::unique_ptr<RooAbsArg>{func.x0});
784 compTermNorm.add(*norm, false);
785
786 compTermNum.add(*func.x1.release());
787 compTermDen.add(*func.x2.release());
788
789 }
790 }
791
792 // WVE THIS NEEDS TO BE REARRANGED
793
794 // compTermset is set van partial integrals to be multiplied
795 // prodtmp = product (compTermSet)
796 // inttmp = int ( prodtmp ) d (outerIntDeps) _range_isetRangeName
797
798 const std::string prodname = makeRGPPName("SPECPROD", compTermSet, outerIntDeps, RooArgSet(), isetRangeName);
799 auto prodtmp = std::make_unique<RooProduct>(prodname.c_str(), prodname.c_str(), compTermSet);
800
801 const std::string intname = makeRGPPName("SPECINT", compTermSet, outerIntDeps, RooArgSet(), isetRangeName);
802 auto inttmp = std::make_unique<RooRealIntegral>(intname.c_str(), intname.c_str(), *prodtmp, outerIntDeps, nullptr, nullptr, isetRangeName);
803 inttmp->setStringAttribute("PROD_TERM_TYPE", "SPECINT");
804
805 cache->_partList.add(*inttmp);
806
807 // Product of numerator terms
808 const string prodname_num = makeRGPPName("SPECPROD_NUM", compTermNum, RooArgSet(), RooArgSet(), nullptr);
809 auto prodtmp_num = std::make_unique<RooProduct>(prodname_num.c_str(), prodname_num.c_str(), compTermNum);
810 prodtmp_num->addOwnedComponents(compTermNum);
811
812 // Product of denominator terms
813 const string prodname_den = makeRGPPName("SPECPROD_DEN", compTermDen, RooArgSet(), RooArgSet(), nullptr);
814 auto prodtmp_den = std::make_unique<RooProduct>(prodname_den.c_str(), prodname_den.c_str(), compTermDen);
815 prodtmp_den->addOwnedComponents(compTermDen);
816
817 // Ratio
818 std::string name = Form("SPEC_RATIO(%s,%s)", prodname_num.c_str(), prodname_den.c_str());
819 auto ndr = std::make_unique<RooFormulaVar>(name.c_str(), "@0/@1", RooArgList(*prodtmp_num, *prodtmp_den));
820
821 // Integral of ratio
822 std::unique_ptr<RooAbsReal> numtmp{ndr->createIntegral(outerIntDeps,isetRangeName)};
823 numtmp->addOwnedComponents(std::move(ndr));
824
825 cache->_ownedList.addOwned(std::move(prodtmp));
826 cache->_ownedList.addOwned(std::move(inttmp));
827 cache->_ownedList.addOwned(std::move(prodtmp_num));
828 cache->_ownedList.addOwned(std::move(prodtmp_den));
829 cache->_numList.addOwned(std::move(numtmp));
830 cache->_denList.addOwned(std::unique_ptr<RooAbsArg>{static_cast<RooAbsArg*>(RooFit::RooConst(1).clone("1"))});
831 cache->_normList.emplace_back(std::make_unique<RooArgSet>());
832 compTermNorm.snapshot(*cache->_normList.back(), false);
833 }
834 }
835
836 // Need to rearrange product in case of multiple ranges
837 if (_normRange.Contains(",")) {
838 rearrangeProduct(*cache);
839 }
840
841 return cache;
842}
843
844
846{
847 // We own contents of all lists filled by factorizeProduct()
848 terms.Delete();
849 ints.Delete();
850 imps.Delete();
851 norms.Delete();
852 cross.Delete();
853}
854
855
856////////////////////////////////////////////////////////////////////////////////
857/// For single normalization ranges
858
859std::unique_ptr<RooAbsReal> RooProdPdf::makeCondPdfRatioCorr(RooAbsReal& pdf, const RooArgSet& termNset, const RooArgSet& /*termImpSet*/, const char* normRangeTmp, const char* refRange) const
860{
861 std::unique_ptr<RooAbsReal> ratio_num{pdf.createIntegral(termNset,normRangeTmp)};
862 std::unique_ptr<RooAbsReal> ratio_den{pdf.createIntegral(termNset,refRange)};
863 auto ratio = std::make_unique<RooFormulaVar>(Form("ratio(%s,%s)",ratio_num->GetName(),ratio_den->GetName()),"@0/@1",
864 RooArgList(*ratio_num,*ratio_den)) ;
865
866 ratio->addOwnedComponents(std::move(ratio_num));
867 ratio->addOwnedComponents(std::move(ratio_den));
868 ratio->setAttribute("RATIO_TERM") ;
869 return ratio ;
870}
871
872
873
874
875////////////////////////////////////////////////////////////////////////////////
876
878{
879 RooArgSet nomList ;
880
881 std::vector<std::string> rangeComps = ROOT::Split(_normRange.Data(), ",", /*skipEmpty=*/true);
882
883 std::map<std::string,RooArgSet> denListList ;
884 RooArgSet specIntDeps ;
885 string specIntRange ;
886
887 for (std::size_t i = 0; i < cache._partList.size(); i++) {
888
889 RooAbsReal *part = static_cast<RooAbsReal*>(cache._partList.at(i));
890 RooAbsReal *num = static_cast<RooAbsReal*>(cache._numList.at(i));
891 RooAbsReal *den = static_cast<RooAbsReal*>(cache._denList.at(i));
892 i++;
893
894
895 RooFormulaVar* ratio(nullptr) ;
896 RooArgSet origNumTerm ;
897
898 if (string("SPECINT")==part->getStringAttribute("PROD_TERM_TYPE")) {
899
900 RooRealIntegral* orig = static_cast<RooRealIntegral*>(num);
901 auto specratio = static_cast<RooFormulaVar const*>(&orig->integrand()) ;
902 RooProduct* func = static_cast<RooProduct*>(specratio->getParameter(0)) ;
903
904 std::unique_ptr<RooArgSet> components{orig->getComponents()};
905 for(RooAbsArg * carg : *components) {
906 if (carg->getAttribute("RATIO_TERM")) {
907 ratio = static_cast<RooFormulaVar*>(carg) ;
908 break ;
909 }
910 }
911
912 if (ratio) {
913 RooCustomizer cust(*func,"blah") ;
914 cust.replaceArg(*ratio,RooFit::RooConst(1)) ;
915 nomList.add(*cust.build()) ;
916 } else {
917 nomList.add(*func) ;
918 }
919
920
921 } else {
922
923 // Find the ratio term
924 RooAbsReal* func = num;
925 // If top level object is integral, navigate to integrand
927 func = const_cast<RooAbsReal*>(&static_cast<RooRealIntegral*>(func)->integrand());
928 }
929 if (func->InheritsFrom(RooProduct::Class())) {
930 for(RooAbsArg * arg : static_cast<RooProduct*>(func)->components()) {
931 if (arg->getAttribute("RATIO_TERM")) {
932 ratio = static_cast<RooFormulaVar*>(arg) ;
933 } else {
934 origNumTerm.add(*arg) ;
935 }
936 }
937 }
938
939 if (ratio) {
940 nomList.add(origNumTerm) ;
941 } else {
942 nomList.add(*num) ;
943 }
944
945 }
946
947 for (auto iter = rangeComps.begin() ; iter != rangeComps.end() ; ++iter) {
948 // If denominator is an integral, make a clone with the integration range adjusted to
949 // the selected component of the normalization integral
950
951 if (string("SPECINT")==part->getStringAttribute("PROD_TERM_TYPE")) {
952
953 RooRealIntegral* orig = static_cast<RooRealIntegral*>(num);
954 auto specRatio = static_cast<RooFormulaVar const*>(&orig->integrand()) ;
955 specIntDeps.add(orig->intVars()) ;
956 if (orig->intRange()) {
957 specIntRange = orig->intRange() ;
958 }
959 //RooProduct* numtmp = (RooProduct*) specRatio->getParameter(0) ;
960 RooProduct* dentmp = static_cast<RooProduct*>(specRatio->getParameter(1)) ;
961
962 for (auto* parg : static_range_cast<RooAbsReal*>(dentmp->components())) {
963 if (ratio && parg->dependsOn(*ratio)) {
964 // Make specialize ratio instance
965 std::unique_ptr<RooAbsReal> specializedRatio{specializeRatio(*(RooFormulaVar*)ratio,iter->c_str())};
966
967 // Replace generic ratio with specialized ratio
968 RooAbsArg *partCust(nullptr) ;
969 if (parg->InheritsFrom(RooAddition::Class())) {
970
971
972
973 RooAddition* tmpadd = static_cast<RooAddition*>(parg) ;
974
975 RooCustomizer cust(*tmpadd->list1().first(), ("blah_" + *iter).c_str());
976 cust.replaceArg(*ratio,*specializedRatio) ;
977 partCust = cust.build() ;
978
979 } else {
980 RooCustomizer cust(*parg, ("blah_" + *iter).c_str());
981 cust.replaceArg(*ratio, *specializedRatio);
982 partCust = cust.build();
983 }
984
985 // Print customized denominator
986
987 std::unique_ptr<RooAbsReal> specializedPartCust{specializeIntegral(*static_cast<RooAbsReal*>(partCust),iter->c_str())};
988
989 // Finally divide again by ratio
990 string name = Form("%s_divided_by_ratio",specializedPartCust->GetName()) ;
991 auto specIntFinal = std::make_unique<RooFormulaVar>(name.c_str(),"@0/@1",RooArgList(*specializedPartCust,*specializedRatio)) ;
992 specIntFinal->addOwnedComponents(std::move(specializedPartCust));
993 specIntFinal->addOwnedComponents(std::move(specializedRatio));
994
995 denListList[*iter].addOwned(std::move(specIntFinal));
996 } else {
997
998 denListList[*iter].addOwned(specializeIntegral(*parg,iter->c_str()));
999
1000 }
1001 }
1002 } else {
1003
1004 if (ratio) {
1005
1006 std::unique_ptr<RooAbsReal> specRatio{specializeRatio(*(RooFormulaVar*)ratio,iter->c_str())};
1007
1008 // If integral is 'Int r(y)*g(y) dy ' then divide a posteriori by r(y)
1009
1010 RooArgSet tmp(origNumTerm) ;
1011 tmp.add(*specRatio) ;
1012 const string pname = makeRGPPName("PROD",tmp,RooArgSet(),RooArgSet(),nullptr) ;
1013 auto specDenProd = std::make_unique<RooProduct>(pname.c_str(),pname.c_str(),tmp) ;
1014 std::unique_ptr<RooAbsReal> specInt;
1015
1016 if (den->InheritsFrom(RooRealIntegral::Class())) {
1017 specInt = std::unique_ptr<RooAbsReal>{specDenProd->createIntegral((static_cast<RooRealIntegral*>(den))->intVars(),iter->c_str())};
1018 specInt->addOwnedComponents(std::move(specDenProd));
1019 } else if (den->InheritsFrom(RooAddition::Class())) {
1020 RooAddition* orig = static_cast<RooAddition*>(den) ;
1021 RooRealIntegral* origInt = static_cast<RooRealIntegral*>(orig->list1().first()) ;
1022 specInt = std::unique_ptr<RooAbsReal>{specDenProd->createIntegral(origInt->intVars(),iter->c_str())};
1023 specInt->addOwnedComponents(std::move(specDenProd));
1024 } else {
1025 throw string("this should not happen") ;
1026 }
1027
1028 //RooAbsReal* specInt = specializeIntegral(*den,iter->c_str()) ;
1029 string name = Form("%s_divided_by_ratio",specInt->GetName()) ;
1030 auto specIntFinal = std::make_unique<RooFormulaVar>(name.c_str(),"@0/@1",RooArgList(*specInt,*specRatio)) ;
1031 specIntFinal->addOwnedComponents(std::move(specInt));
1032 specIntFinal->addOwnedComponents(std::move(specRatio));
1033 denListList[*iter].addOwned(std::move(specIntFinal));
1034 } else {
1035 denListList[*iter].addOwned(specializeIntegral(*den,iter->c_str()));
1036 }
1037
1038 }
1039 }
1040
1041 }
1042
1043 // Do not rearrange terms if numerator and denominator are effectively empty
1044 if (nomList.empty()) {
1045 return ;
1046 }
1047
1048 string name = std::string{GetName()} + "_numerator";
1049 // WVE FIX THIS (2)
1050
1051 std::unique_ptr<RooAbsReal> numerator = std::make_unique<RooProduct>(name.c_str(),name.c_str(),nomList) ;
1052
1053 RooArgSet products ;
1054 for (map<string,RooArgSet>::iterator iter = denListList.begin() ; iter != denListList.end() ; ++iter) {
1055 name = Form("%s_denominator_comp_%s",GetName(),iter->first.c_str()) ;
1056 // WVE FIX THIS (2)
1057 RooProduct* prod_comp = new RooProduct(name.c_str(),name.c_str(),iter->second) ;
1058 prod_comp->addOwnedComponents(std::move(iter->second));
1059 products.add(*prod_comp) ;
1060 }
1061 name = Form("%s_denominator_sum",GetName()) ;
1062 RooAbsReal* norm = new RooAddition(name.c_str(),name.c_str(),products) ;
1063 norm->addOwnedComponents(products) ;
1064
1065 if (!specIntDeps.empty()) {
1066 // Apply posterior integration required for SPECINT case
1067
1068 string namesr = Form("SPEC_RATIO(%s,%s)",numerator->GetName(),norm->GetName()) ;
1069 RooFormulaVar* ndr = new RooFormulaVar(namesr.c_str(),"@0/@1",RooArgList(*numerator,*norm)) ;
1070 ndr->addOwnedComponents(std::move(numerator));
1071
1072 // Integral of ratio
1073 numerator = std::unique_ptr<RooAbsReal>{ndr->createIntegral(specIntDeps,specIntRange.c_str())};
1074
1075 norm = static_cast<RooAbsReal*>(RooFit::RooConst(1).Clone()) ;
1076 }
1077
1078
1079
1080 // WVE DEBUG
1081 //RooMsgService::instance().debugWorkspace()->import(RooArgSet(*numerator,*norm)) ;
1082
1083 cache._rearrangedNum = std::move(numerator);
1084 cache._rearrangedDen.reset(norm);
1085 cache._isRearranged = true ;
1086
1087}
1088
1089
1090////////////////////////////////////////////////////////////////////////////////
1091
1092std::unique_ptr<RooAbsReal> RooProdPdf::specializeRatio(RooFormulaVar& input, const char* targetRangeName) const
1093{
1094 RooRealIntegral* numint = static_cast<RooRealIntegral*>(input.getParameter(0)) ;
1095 RooRealIntegral* denint = static_cast<RooRealIntegral*>(input.getParameter(1)) ;
1096
1097 std::unique_ptr<RooAbsReal> numint_spec{specializeIntegral(*numint,targetRangeName)};
1098
1099 std::unique_ptr<RooAbsReal> ret = std::make_unique<RooFormulaVar>(Form("ratio(%s,%s)",numint_spec->GetName(),denint->GetName()),"@0/@1",RooArgList(*numint_spec,*denint)) ;
1100 ret->addOwnedComponents(std::move(numint_spec));
1101
1102 return ret;
1103}
1104
1105
1106
1107////////////////////////////////////////////////////////////////////////////////
1108
1109std::unique_ptr<RooAbsReal> RooProdPdf::specializeIntegral(RooAbsReal& input, const char* targetRangeName) const
1110{
1111 if (input.InheritsFrom(RooRealIntegral::Class())) {
1112
1113 // If input is integral, recreate integral but override integration range to be targetRangeName
1114 RooRealIntegral* orig = static_cast<RooRealIntegral*>(&input) ;
1115 return std::unique_ptr<RooAbsReal>{orig->integrand().createIntegral(orig->intVars(),targetRangeName)};
1116
1117 } else if (input.InheritsFrom(RooAddition::Class())) {
1118
1119 // If input is sum of integrals, recreate integral from first component of set, but override integration range to be targetRangeName
1120 RooAddition* orig = static_cast<RooAddition*>(&input) ;
1121 RooRealIntegral* origInt = static_cast<RooRealIntegral*>(orig->list1().first()) ;
1122 return std::unique_ptr<RooAbsReal>{origInt->integrand().createIntegral(origInt->intVars(),targetRangeName)};
1123 }
1124
1125 std::stringstream errMsg;
1126 errMsg << "specializeIntegral: unknown input type " << input.ClassName() << "::" << input.GetName();
1127 throw std::runtime_error(errMsg.str());
1128}
1129
1130
1131////////////////////////////////////////////////////////////////////////////////
1132/// Group product into terms that can be calculated independently
1133
1134void RooProdPdf::groupProductTerms(std::list<std::vector<RooArgSet*>>& groupedTerms, RooArgSet& outerIntDeps,
1135 Factorized const &factorized) const
1136{
1137 // Start out with each term in its own group
1138 for(auto * term : static_range_cast<RooArgSet*>(factorized.terms)) {
1139 groupedTerms.emplace_back();
1140 groupedTerms.back().emplace_back(term) ;
1141 }
1142
1143 // Make list of imported dependents that occur in any term
1144 RooArgSet allImpDeps ;
1145 for(auto * impDeps : static_range_cast<RooArgSet*>(factorized.imps)) {
1146 allImpDeps.add(*impDeps,false) ;
1147 }
1148
1149 // Make list of integrated dependents that occur in any term
1150 RooArgSet allIntDeps ;
1151 for(auto * intDeps : static_range_cast<RooArgSet*>(factorized.ints)) {
1152 allIntDeps.add(*intDeps,false) ;
1153 }
1154
1155 outerIntDeps.removeAll() ;
1156 outerIntDeps.add(*std::unique_ptr<RooArgSet>{allIntDeps.selectCommon(allImpDeps)});
1157
1158 // Now iteratively merge groups that should be (partially) integrated together
1159 for(RooAbsArg * outerIntDep : outerIntDeps) {
1160
1161 // Collect groups that feature this dependent
1162 std::vector<RooArgSet*>* newGroup = nullptr ;
1163
1164 // Loop over groups
1165 bool needMerge = false ;
1166 auto group = groupedTerms.begin();
1167 auto nGroups = groupedTerms.size();
1168 for (size_t iGroup = 0; iGroup < nGroups; ++iGroup) {
1169
1170 // See if any term in this group depends in any ay on outerDepInt
1171 for (auto const& term2 : *group) {
1172
1173 Int_t termIdx = factorized.terms.IndexOf(term2) ;
1174 if (factorized.termNormDeps(termIdx)->contains(*outerIntDep) ||
1175 factorized.termIntDeps(termIdx)->contains(*outerIntDep) ||
1176 factorized.termImpDeps(termIdx)->contains(*outerIntDep)) {
1177 needMerge = true ;
1178 }
1179
1180 }
1181
1182 if (needMerge) {
1183 // Create composite group if not yet existing
1184 if (newGroup==nullptr) {
1185 groupedTerms.emplace_back() ;
1186 newGroup = &groupedTerms.back() ;
1187 }
1188
1189 // Add terms of this group to new term
1190 for (auto& term2 : *group) {
1191 newGroup->emplace_back(term2) ;
1192 }
1193
1194 // Remove this non-owning group from list
1195 group = groupedTerms.erase(group);
1196 } else {
1197 ++group;
1198 }
1199 }
1200
1201 }
1202}
1203
1204
1205
1206////////////////////////////////////////////////////////////////////////////////
1207/// Calculate integrals of factorized product terms over observables iset while normalized
1208/// to observables in nset.
1209
1211 const RooArgSet* term,const RooArgSet& termNSet, const RooArgSet& termISet,
1212 bool forceWrap) const
1213{
1215
1216 // CASE I: factorizing term: term is integrated over all normalizing observables
1217 // -----------------------------------------------------------------------------
1218 // Check if all observbales of this term are integrated. If so the term cancels
1219 if (!termNSet.empty() && termNSet.size()==termISet.size() && isetRangeName==nullptr) {
1220 // Term factorizes
1221 return ret ;
1222 }
1223
1224 // CASE II: Dropped terms: if term is entirely unnormalized, it should be dropped
1225 // ------------------------------------------------------------------------------
1226 if (nset && termNSet.empty()) {
1227 // Drop terms that are not asked to be normalized
1228 return ret ;
1229 }
1230
1231 if (iset && !termISet.empty()) {
1232 if (term->size()==1) {
1233
1234 // CASE IIIa: Normalized and partially integrated single PDF term
1235 //---------------------------------------------------------------
1236
1237 RooAbsPdf* pdf = static_cast<RooAbsPdf*>(term->first()) ;
1238
1239 ret.x0 = std::unique_ptr<RooAbsReal>{pdf->createIntegral(termISet,termNSet,isetRangeName)}.release();
1240 ret.x0->setOperMode(operMode()) ;
1241 ret.x0->setStringAttribute("PROD_TERM_TYPE","IIIa") ;
1242
1243 ret.isOwned=true ;
1244
1245 // Split mode results
1246 ret.x1 = std::unique_ptr<RooAbsReal>{pdf->createIntegral(termISet,isetRangeName)};
1247 ret.x2 = std::unique_ptr<RooAbsReal>{pdf->createIntegral(termNSet,normRange())};
1248
1249 return ret ;
1250
1251 } else {
1252
1253 // CASE IIIb: Normalized and partially integrated composite PDF term
1254 //---------------------------------------------------------------
1255
1256 // Use auxiliary class RooGenProdProj to calculate this term
1257 const std::string name = makeRGPPName("GENPROJ_",*term,termISet,termNSet,isetRangeName) ;
1258 ret.x0 = new RooGenProdProj(name.c_str(),name.c_str(),*term,termISet,termNSet,isetRangeName) ;
1259 ret.x0->setStringAttribute("PROD_TERM_TYPE","IIIb") ;
1260 ret.x0->setOperMode(operMode()) ;
1261
1262 ret.isOwned=true ;
1263
1264 const std::string name1 = makeRGPPName("PROD",*term,RooArgSet(),RooArgSet(),nullptr) ;
1265
1266 // WVE FIX THIS
1267 RooProduct* tmp_prod = new RooProduct(name1.c_str(),name1.c_str(),*term) ;
1268
1269 ret.x1 = std::unique_ptr<RooAbsReal>{tmp_prod->createIntegral(termISet,isetRangeName)};
1270 ret.x2 = std::unique_ptr<RooAbsReal>{tmp_prod->createIntegral(termNSet,normRange())};
1271
1272 return ret ;
1273 }
1274 }
1275
1276 // CASE IVa: Normalized non-integrated composite PDF term
1277 // -------------------------------------------------------
1278 if (nset && !nset->empty() && term->size()>1) {
1279 // Composite term needs normalized integration
1280
1281 const std::string name = makeRGPPName("GENPROJ_",*term,termISet,termNSet,isetRangeName) ;
1282 ret.x0 = new RooGenProdProj(name.c_str(),name.c_str(),*term,termISet,termNSet,isetRangeName,normRange()) ;
1283 ret.x0->setExpensiveObjectCache(expensiveObjectCache()) ;
1284
1285 ret.x0->setStringAttribute("PROD_TERM_TYPE","IVa") ;
1286 ret.x0->setOperMode(operMode()) ;
1287
1288 ret.isOwned=true ;
1289
1290 const std::string name1 = makeRGPPName("PROD",*term,RooArgSet(),RooArgSet(),nullptr) ;
1291
1292 // WVE FIX THIS
1293 RooProduct* tmp_prod = new RooProduct(name1.c_str(),name1.c_str(),*term) ;
1294
1295 ret.x1 = std::unique_ptr<RooAbsReal>{tmp_prod->createIntegral(termISet,isetRangeName)};
1296 ret.x2 = std::unique_ptr<RooAbsReal>{tmp_prod->createIntegral(termNSet,normRange())};
1297
1298 return ret ;
1299 }
1300
1301 // CASE IVb: Normalized, non-integrated single PDF term
1302 // -----------------------------------------------------
1303 for (auto* pdf : static_range_cast<RooAbsPdf*>(*term)) {
1304
1305 ret.isOwned = false;
1306 RooAbsReal *ret0 = pdf;
1307
1308 if (forceWrap) {
1309 ret.isOwned = true;
1310 // Construct representative name of normalization wrapper
1311 std::string name = pdf->GetName() + ("_NORM[" + RooHelpers::getColonSeparatedNameString(termNSet, ','));
1312 name += normRange() ? ('|' + std::string{normRange()} + ']') : "]";
1313 ret0 = new RooRealIntegral(name.c_str(),name.c_str(),*pdf,RooArgSet(),&termNSet);
1314 }
1315
1316 ret0->setStringAttribute("PROD_TERM_TYPE","IVb") ;
1317 ret.x0 = ret0;
1318 ret.x1 = std::unique_ptr<RooAbsReal>{pdf->createIntegral(RooArgSet())};
1319 ret.x2 = std::unique_ptr<RooAbsReal>{pdf->createIntegral(termNSet,normRange())};
1320 return ret;
1321 }
1322
1323 coutE(Eval) << "RooProdPdf::processProductTerm(" << GetName() << ") unidentified term!!!" << std::endl ;
1324 return ret ;
1325}
1326
1327
1328
1329
1330////////////////////////////////////////////////////////////////////////////////
1331/// Make an appropriate automatic name for a RooGenProdProj object in getPartIntList()
1332
1333std::string RooProdPdf::makeRGPPName(const char* pfx, const RooArgSet& term, const RooArgSet& iset,
1334 const RooArgSet& nset, const char* isetRangeName) const
1335{
1336 // Make an appropriate automatic name for a RooGenProdProj object in getPartIntList()
1337
1338 std::ostringstream os;
1339 os << pfx;
1340 os << "[";
1341
1342 // Encode component names
1343 bool first(true) ;
1344 for (auto const* pdf : static_range_cast<RooAbsPdf*>(term)) {
1345 if (!first) os << "_X_";
1346 first = false;
1347 os << pdf->GetName();
1348 }
1349 os << "]" << integralNameSuffix(iset,&nset,isetRangeName,true);
1350
1351 return os.str();
1352}
1353
1354
1355
1356////////////////////////////////////////////////////////////////////////////////
1357/// Force RooRealIntegral to offer all observables for internal integration
1358
1360{
1361 return true ;
1362}
1363
1364
1365
1366////////////////////////////////////////////////////////////////////////////////
1367/// Determine which part (if any) of given integral can be performed analytically.
1368/// If any analytical integration is possible, return integration scenario code.
1369///
1370/// RooProdPdf implements two strategies in implementing analytical integrals
1371///
1372/// First, PDF components whose entire set of dependents are requested to be integrated
1373/// can be dropped from the product, as they will integrate out to 1 by construction
1374///
1375/// Second, RooProdPdf queries each remaining component PDF for its analytical integration
1376/// capability of the requested set ('allVars'). It finds the largest common set of variables
1377/// that can be integrated by all remaining components. If such a set exists, it reconfirms that
1378/// each component is capable of analytically integrating the common set, and combines the components
1379/// individual integration codes into a single integration code valid for RooProdPdf.
1380
1382 const RooArgSet* normSet, const char* rangeName) const
1383{
1384 if (_forceNumInt) return 0 ;
1385
1386 // Declare that we can analytically integrate all requested observables
1387 analVars.add(allVars) ;
1388
1389 // Retrieve (or create) the required partial integral list
1390 Int_t code = getPartIntList(normSet,&allVars,rangeName);
1391
1392 return code+1 ;
1393}
1394
1395
1396
1397
1398////////////////////////////////////////////////////////////////////////////////
1399/// Return analytical integral defined by given scenario code
1400
1401double RooProdPdf::analyticalIntegralWN(Int_t code, const RooArgSet* normSet, const char* rangeName) const
1402{
1403 // No integration scenario
1404 if (code==0) {
1405 return getVal(normSet) ;
1406 }
1407
1408
1409 // WVE needs adaptation for rangename feature
1410
1411 // Partial integration scenarios
1412 CacheElem* cache = static_cast<CacheElem*>(_cacheMgr.getObjByIndex(code-1)) ;
1413
1414 // If cache has been sterilized, revive this slot
1415 if (cache==nullptr) {
1416 std::unique_ptr<RooArgSet> vars{getParameters(RooArgSet())} ;
1417 RooArgSet nset = _cacheMgr.selectFromSet1(*vars, code-1) ;
1418 RooArgSet iset = _cacheMgr.selectFromSet2(*vars, code-1) ;
1419
1420 Int_t code2 = getPartIntList(&nset, &iset, rangeName) ;
1421
1422 // preceding call to getPartIntList guarantees non-null return
1423 // coverity[NULL_RETURNS]
1424 cache = static_cast<CacheElem*>(_cacheMgr.getObj(&nset,&iset,&code2,rangeName)) ;
1425 }
1426
1427 double val = calculate(*cache,true) ;
1428
1429 return val ;
1430}
1431
1432
1433
1434////////////////////////////////////////////////////////////////////////////////
1435/// If this product contains exactly one extendable p.d.f return the extension abilities of
1436/// that p.d.f, otherwise return CanNotBeExtended
1437
1439{
1440 return (_extendedIndex>=0) ? (static_cast<RooAbsPdf*>(_pdfList.at(_extendedIndex)))->extendMode() : CanNotBeExtended ;
1441}
1442
1443
1444
1445////////////////////////////////////////////////////////////////////////////////
1446/// Return the expected number of events associated with the extendable input PDF
1447/// in the product. If there is no extendable term, abort.
1448
1449double RooProdPdf::expectedEvents(const RooArgSet* nset) const
1450{
1451 if (_extendedIndex<0) {
1452 coutF(Generation) << "Requesting expected number of events from a RooProdPdf that does not contain an extended p.d.f" << std::endl ;
1453 throw std::logic_error(std::string("RooProdPdf ") + GetName() + " could not be extended.");
1454 }
1455
1456 return static_cast<RooAbsPdf*>(_pdfList.at(_extendedIndex))->expectedEvents(nset) ;
1457}
1458
1459std::unique_ptr<RooAbsReal> RooProdPdf::createExpectedEventsFunc(const RooArgSet* nset) const
1460{
1461 if (_extendedIndex<0) {
1462 coutF(Generation) << "Requesting expected number of events from a RooProdPdf that does not contain an extended p.d.f" << std::endl ;
1463 throw std::logic_error(std::string("RooProdPdf ") + GetName() + " could not be extended.");
1464 }
1465
1466 return static_cast<RooAbsPdf*>(_pdfList.at(_extendedIndex))->createExpectedEventsFunc(nset);
1467}
1468
1469
1470////////////////////////////////////////////////////////////////////////////////
1471/// Return generator context optimized for generating events from product p.d.f.s
1472
1474 const RooArgSet* auxProto, bool verbose) const
1475{
1476 if (_useDefaultGen) return RooAbsPdf::genContext(vars,prototype,auxProto,verbose) ;
1477 return new RooProdGenContext(*this,vars,prototype,auxProto,verbose) ;
1478}
1479
1480
1481
1482////////////////////////////////////////////////////////////////////////////////
1483/// Query internal generation capabilities of component p.d.f.s and aggregate capabilities
1484/// into master configuration passed to the generator context
1485
1486Int_t RooProdPdf::getGenerator(const RooArgSet& directVars, RooArgSet &generateVars, bool staticInitOK) const
1487{
1488 if (!_useDefaultGen) return 0 ;
1489
1490 // Find the subset directVars that only depend on a single PDF in the product
1491 RooArgSet directSafe ;
1492 for (auto const* arg : directVars) {
1493 if (isDirectGenSafe(*arg)) directSafe.add(*arg) ;
1494 }
1495
1496
1497 // Now find direct integrator for relevant components ;
1498 std::vector<Int_t> code;
1499 code.reserve(64);
1500 for (auto const* pdf : static_range_cast<RooAbsPdf*>(_pdfList)) {
1501 RooArgSet pdfDirect ;
1502 Int_t pdfCode = pdf->getGenerator(directSafe,pdfDirect,staticInitOK);
1503 code.push_back(pdfCode);
1504 if (pdfCode != 0) {
1505 generateVars.add(pdfDirect) ;
1506 }
1507 }
1508
1509
1510 if (!generateVars.empty()) {
1511 auto found = std::find(_genCode.begin(), _genCode.end(), code);
1512 // If a generator for the codes was already cached, return the index to the
1513 // corresponding caching index plus one.
1514 if (found != _genCode.end()) {
1515 return std::distance(_genCode.begin(), found) + 1;
1516 }
1517 _genCode.emplace_back(std::move(code));
1518 return _genCode.size();
1519 }
1520 return 0;
1521}
1522
1523
1524
1525////////////////////////////////////////////////////////////////////////////////
1526/// Forward one-time initialization call to component generation initialization
1527/// methods.
1528
1530{
1531 if (!_useDefaultGen) return ;
1532
1533 const std::vector<int>& codeList = _genCode[code-1];
1534 Int_t i(0) ;
1535 for (auto* pdf : static_range_cast<RooAbsPdf*>(_pdfList)) {
1536 if (codeList[i]!=0) {
1537 pdf->initGenerator(codeList[i]) ;
1538 }
1539 i++ ;
1540 }
1541}
1542
1543
1544
1545////////////////////////////////////////////////////////////////////////////////
1546/// Generate a single event with configuration specified by 'code'
1547/// Defer internal generation to components as encoded in the _genCode
1548/// registry for given generator code.
1549
1551{
1552 if (!_useDefaultGen) return ;
1553
1554 const std::vector<Int_t>& codeList = _genCode[code-1];
1555 Int_t i(0) ;
1556 for (auto* pdf : static_range_cast<RooAbsPdf*>(_pdfList)) {
1557 if (codeList[i]!=0) {
1558 pdf->generateEvent(codeList[i]) ;
1559 }
1560 i++ ;
1561 }
1562}
1563
1564
1565
1566////////////////////////////////////////////////////////////////////////////////
1567/// Return RooAbsArg components contained in the cache
1568
1570{
1571 RooArgList ret ;
1572 ret.add(_partList) ;
1573 ret.add(_numList) ;
1574 ret.add(_denList) ;
1575 if (_rearrangedNum) ret.add(*_rearrangedNum) ;
1576 if (_rearrangedDen) ret.add(*_rearrangedDen) ;
1577 return ret ;
1578
1579}
1580
1581
1582
1583////////////////////////////////////////////////////////////////////////////////
1584/// Hook function to print cache contents in tree printing of RooProdPdf
1585
1586void RooProdPdf::CacheElem::printCompactTreeHook(ostream& os, const char* indent, Int_t curElem, Int_t maxElem)
1587{
1588 if (curElem==0) {
1589 os << indent << "RooProdPdf begin partial integral cache" << std::endl ;
1590 }
1591
1592 auto indent2 = std::string(indent) + "[" + std::to_string(curElem) + "]";
1593 for(auto const& arg : _partList) {
1594 arg->printCompactTree(os,indent2.c_str()) ;
1595 }
1596
1597 if (curElem==maxElem) {
1598 os << indent << "RooProdPdf end partial integral cache" << std::endl ;
1599 }
1600}
1601
1602
1603
1604////////////////////////////////////////////////////////////////////////////////
1605/// Forward determination of safety of internal generator code to
1606/// component p.d.f that would generate the given observable
1607
1609{
1610 // Only override base class behaviour if default generator method is enabled
1611 if (!_useDefaultGen) return RooAbsPdf::isDirectGenSafe(arg) ;
1612
1613 // Argument may appear in only one PDF component
1614 RooAbsPdf* thePdf(nullptr) ;
1615 for (auto* pdf : static_range_cast<RooAbsPdf*>(_pdfList)) {
1616
1617 if (pdf->dependsOn(arg)) {
1618 // Found PDF depending on arg
1619
1620 // If multiple PDFs depend on arg directGen is not safe
1621 if (thePdf) return false ;
1622
1623 thePdf = pdf ;
1624 }
1625 }
1626 // Forward call to relevant component PDF
1627 return thePdf?(thePdf->isDirectGenSafe(arg)):false ;
1628}
1629
1630
1631
1632////////////////////////////////////////////////////////////////////////////////
1633/// Look up user specified normalization set for given input PDF component
1634
1636{
1637 Int_t idx = _pdfList.index(&pdf);
1638 return idx < 0 ? nullptr : _pdfNSetList[idx].get();
1639}
1640
1641
1642
1643/// Add some full PDFs to the factors of this RooProdPdf.
1645{
1646 size_t numExtended = (_extendedIndex==-1) ? 0 : 1;
1647
1648 for(auto arg : pdfs) {
1649 RooAbsPdf* pdf = dynamic_cast<RooAbsPdf*>(arg);
1650 if (!pdf) {
1651 coutW(InputArguments) << "RooProdPdf::addPdfs(" << GetName() << ") list arg "
1652 << arg->GetName() << " is not a PDF, ignored" << std::endl ;
1653 continue;
1654 }
1655 if(pdf->canBeExtended()) {
1656 if (_extendedIndex == -1) {
1658 } else {
1659 numExtended++;
1660 }
1661 }
1662 _pdfList.add(*pdf);
1663 _pdfNSetList.emplace_back(std::make_unique<RooArgSet>("nset"));
1664 }
1665
1666 // Protect against multiple extended terms
1667 if (numExtended>1) {
1668 coutW(InputArguments) << "RooProdPdf::addPdfs(" << GetName()
1669 << ") WARNING: multiple components with extended terms detected,"
1670 << " product will not be extendable." << std::endl ;
1671 _extendedIndex = -1 ;
1672 }
1673
1674 // Reset cache
1675 _cacheMgr.reset() ;
1676
1677}
1678
1679/// Remove some PDFs from the factors of this RooProdPdf.
1681{
1682 // Remember what the extended PDF is
1683 RooAbsArg const* extPdf = _extendedIndex >= 0 ? &_pdfList[_extendedIndex] : nullptr;
1684
1685 // Actually remove the PDFs and associated nsets
1686 for(size_t i=0;i < _pdfList.size(); i++) {
1687 if(pdfs.contains(_pdfList[i])) {
1689 _pdfNSetList.erase(_pdfNSetList.begin()+i);
1690 i--;
1691 }
1692 }
1693
1694 // Since we may have removed PDFs from the list, the index of the extended
1695 // PDF in the list needs to be updated. The new index might also be -1 if the
1696 // extended PDF got removed.
1697 if(extPdf) {
1698 _extendedIndex = _pdfList.index(*extPdf);
1699 }
1700
1701 // Reset cache
1702 _cacheMgr.reset() ;
1703}
1704
1705
1706namespace {
1707
1708std::vector<TNamed const*> sortedNamePtrs(RooAbsCollection const& col)
1709{
1710 std::vector<TNamed const*> ptrs;
1711 ptrs.reserve(col.size());
1712 for(RooAbsArg* arg : col) {
1713 ptrs.push_back(arg->namePtr());
1714 }
1715 std::sort(ptrs.begin(), ptrs.end());
1716 return ptrs;
1717}
1718
1719bool sortedNamePtrsOverlap(std::vector<TNamed const*> const& ptrsA, std::vector<TNamed const*> const& ptrsB)
1720{
1721 auto pA = ptrsA.begin();
1722 auto pB = ptrsB.begin();
1723 while (pA != ptrsA.end() && pB != ptrsB.end()) {
1724 if (*pA < *pB) {
1725 ++pA;
1726 } else if (*pB < *pA) {
1727 ++pB;
1728 } else {
1729 return true;
1730 }
1731 }
1732 return false;
1733}
1734
1735} // namespace
1736
1737
1738////////////////////////////////////////////////////////////////////////////////
1739/// Return all parameter constraint p.d.f.s on parameters listed in constrainedParams.
1740/// The observables set is required to distinguish unambiguously p.d.f in terms
1741/// of observables and parameters, which are not constraints, and p.d.fs in terms
1742/// of parameters only, which can serve as constraints p.d.f.s
1743/// The pdfParams output parameter communicates to the caller which parameter
1744/// are used in the pdfs that are not constraints.
1745
1746std::unique_ptr<RooArgSet>
1747RooProdPdf::getConstraints(const RooArgSet &observables, RooArgSet const &constrainedParams, RooArgSet &pdfParams) const
1748{
1749 auto constraints = std::make_unique<RooArgSet>("constraints");
1750
1751 // For the optimized implementation of checking if two collections overlap by name.
1752 auto observablesNamePtrs = sortedNamePtrs(observables);
1753 auto constrainedParamsNamePtrs = sortedNamePtrs(constrainedParams);
1754
1755 // Loop over PDF components
1756 for (std::size_t iPdf = 0; iPdf < _pdfList.size(); ++iPdf) {
1757 auto * pdf = static_cast<RooAbsPdf*>(&_pdfList[iPdf]);
1758
1759 RooArgSet tmp;
1760 pdf->getParameters(nullptr, tmp);
1761
1762 // A constraint term is a p.d.f that doesn't contribute to the
1763 // expectedEvents() and does not depend on any of the listed observables
1764 // but does depends on any of the parameters that should be constrained
1765 bool isConstraint = false;
1766
1767 if(static_cast<int>(iPdf) != _extendedIndex) {
1768 auto tmpNamePtrs = sortedNamePtrs(tmp);
1769 // Before, there were calls to `pdf->dependsOn()` here, but they were very
1770 // expensive for large computation graphs! Given that we have to traverse
1771 // the computation graph with a call to `pdf->getParameters()` anyway, we
1772 // can just check if the set of all variables operlaps with the observables
1773 // or constraind parameters.
1774 //
1775 // We are using an optimized implementation of overlap checking. Because
1776 // the overlap is checked by name, we can check overlap of the
1777 // corresponding name pointers. The optimization can't be in
1778 // RooAbsCollection itself, because it is crucial that the memory for the
1779 // non-tmp name pointers is not reallocated for each pdf.
1780 isConstraint = !sortedNamePtrsOverlap(tmpNamePtrs, observablesNamePtrs) &&
1781 sortedNamePtrsOverlap(tmpNamePtrs, constrainedParamsNamePtrs);
1782 }
1783 if (isConstraint) {
1784 constraints->add(*pdf) ;
1785 } else {
1786 // We only want to add parameter, not observables. Since a call like
1787 // `pdf->getParameters(&observables)` would be expensive, we take the set
1788 // of all variables and remove the ovservables, which is much cheaper. In
1789 // a call to `pdf->getParameters(&observables)`, the observables are
1790 // matched by name, so we have to pass the `matchByNameOnly` here.
1791 tmp.remove(observables, /*silent=*/false, /*matchByNameOnly=*/true);
1792 pdfParams.add(tmp,true) ;
1793 }
1794 }
1795
1796 return constraints;
1797}
1798
1799
1800
1801
1802////////////////////////////////////////////////////////////////////////////////
1803/// Return all parameter constraint p.d.f.s on parameters listed in constrainedParams.
1804/// The observables set is required to distinguish unambiguously p.d.f in terms
1805/// of observables and parameters, which are not constraints, and p.d.fs in terms
1806/// of parameters only, which can serve as constraints p.d.f.s
1807
1809{
1810 RooArgSet* connectedPars = new RooArgSet("connectedPars") ;
1811 for (std::size_t iPdf = 0; iPdf < _pdfList.size(); ++iPdf) {
1812 auto * pdf = static_cast<RooAbsPdf*>(&_pdfList[iPdf]);
1813 // Check if term is relevant, either because it provides a propablity
1814 // density in the observables or because it is used for the expected
1815 // events.
1816 if (static_cast<int>(iPdf) == _extendedIndex || pdf->dependsOn(observables)) {
1817 RooArgSet tmp;
1818 pdf->getParameters(&observables, tmp);
1819 connectedPars->add(tmp) ;
1820 }
1821 }
1822 return connectedPars ;
1823}
1824
1825////////////////////////////////////////////////////////////////////////////////
1826/// Interface function used by test statistics to freeze choice of range
1827/// for interpretation of conditional product terms
1828
1829void RooProdPdf::selectNormalizationRange(const char* rangeName, bool force)
1830{
1831 if (!force && _refRangeName) {
1832 return ;
1833 }
1834
1835 fixRefRange(rangeName) ;
1836}
1837
1838
1839
1840
1841////////////////////////////////////////////////////////////////////////////////
1842
1843void RooProdPdf::fixRefRange(const char* rangeName)
1844{
1845 _refRangeName = const_cast<TNamed*>(RooNameReg::ptr(rangeName));
1846}
1847
1848
1849
1850////////////////////////////////////////////////////////////////////////////////
1851/// Forward the plot sampling hint from the p.d.f. that defines the observable obs
1852
1853std::list<double>* RooProdPdf::plotSamplingHint(RooAbsRealLValue& obs, double xlo, double xhi) const
1854{
1855 for (auto const* pdf : static_range_cast<RooAbsPdf*>(_pdfList)) {
1856 if (std::list<double>* hint = pdf->plotSamplingHint(obs,xlo,xhi)) {
1857 return hint ;
1858 }
1859 }
1860
1861 return nullptr;
1862}
1863
1864
1865
1866////////////////////////////////////////////////////////////////////////////////
1867/// If all components that depend on obs are binned that so is the product
1868
1870{
1871 for (auto const* pdf : static_range_cast<RooAbsPdf*>(_pdfList)) {
1872 if (pdf->dependsOn(obs) && !pdf->isBinnedDistribution(obs)) {
1873 return false ;
1874 }
1875 }
1876
1877 return true ;
1878}
1879
1880
1881
1882
1883
1884
1885////////////////////////////////////////////////////////////////////////////////
1886/// Forward the plot sampling hint from the p.d.f. that defines the observable obs
1887
1888std::list<double>* RooProdPdf::binBoundaries(RooAbsRealLValue& obs, double xlo, double xhi) const
1889{
1890 for (auto const* pdf : static_range_cast<RooAbsPdf*>(_pdfList)) {
1891 if (std::list<double>* hint = pdf->binBoundaries(obs,xlo,xhi)) {
1892 return hint ;
1893 }
1894 }
1895
1896 return nullptr;
1897}
1898
1899
1900
1901////////////////////////////////////////////////////////////////////////////////
1902/// Customized printing of arguments of a RooProdPdf to more intuitively reflect the contents of the
1903/// product operator construction
1904
1905void RooProdPdf::printMetaArgs(ostream& os) const
1906{
1907 for (std::size_t i=0 ; i<_pdfList.size() ; i++) {
1908 if (i>0) os << " * " ;
1909 RooArgSet* ncset = _pdfNSetList[i].get() ;
1910 os << _pdfList.at(i)->GetName() ;
1911 if (!ncset->empty()) {
1912 if (string("nset")==ncset->GetName()) {
1913 os << *ncset ;
1914 } else {
1915 os << "|" ;
1916 bool first(true) ;
1917 for (auto const* arg : *ncset) {
1918 if (!first) {
1919 os << "," ;
1920 } else {
1921 first = false ;
1922 }
1923 os << arg->GetName() ;
1924 }
1925 }
1926 }
1927 }
1928 os << " " ;
1929}
1930
1931
1932
1933////////////////////////////////////////////////////////////////////////////////
1934/// Implement support for node removal
1935
1936bool RooProdPdf::redirectServersHook(const RooAbsCollection& newServerList, bool mustReplaceAll, bool nameChange, bool isRecursive)
1937{
1938 if (nameChange && _pdfList.find("REMOVAL_DUMMY")) {
1939
1940 cxcoutD(LinkStateMgmt) << "RooProdPdf::redirectServersHook(" << GetName() << "): removing REMOVAL_DUMMY" << std::endl ;
1941
1942 // Remove node from _pdfList proxy and remove corresponding entry from normset list
1943 RooAbsArg* pdfDel = _pdfList.find("REMOVAL_DUMMY") ;
1944
1945 _pdfNSetList.erase(_pdfNSetList.begin() + _pdfList.index("REMOVAL_DUMMY")) ;
1946 _pdfList.remove(*pdfDel) ;
1947
1948 // Clear caches
1949 _cacheMgr.reset() ;
1950 }
1951
1952 // If the replaced server is an observable that is used in any of the
1953 // normalization sets for conditional fits, replace the element in the
1954 // normalization set too.
1955 for(std::unique_ptr<RooArgSet> const& normSet : _pdfNSetList) {
1956 for(RooAbsArg * arg : *normSet) {
1957 if(RooAbsArg * newArg = arg->findNewServer(newServerList, nameChange)) {
1958 // Since normSet is owning, the original arg is now deleted.
1959 normSet->replace(arg, std::unique_ptr<RooAbsArg>{newArg->cloneTree()});
1960 }
1961 }
1962 }
1963
1964 return RooAbsPdf::redirectServersHook(newServerList, mustReplaceAll, nameChange, isRecursive);
1965}
1966
1967void RooProdPdf::CacheElem::writeToStream(std::ostream& os) const {
1968 using namespace RooHelpers;
1969 os << "_partList\n";
1970 os << getColonSeparatedNameString(_partList) << "\n";
1971 os << "_numList\n";
1972 os << getColonSeparatedNameString(_numList) << "\n";
1973 os << "_denList\n";
1974 os << getColonSeparatedNameString(_denList) << "\n";
1975 os << "_ownedList\n";
1976 os << getColonSeparatedNameString(_ownedList) << "\n";
1977 os << "_normList\n";
1978 for(auto const& set : _normList) {
1979 os << getColonSeparatedNameString(*set) << "\n";
1980 }
1981 os << "_isRearranged" << "\n";
1982 os << _isRearranged << "\n";
1983 os << "_rearrangedNum" << "\n";
1984 if(_rearrangedNum) {
1985 os << getColonSeparatedNameString(*_rearrangedNum) << "\n";
1986 } else {
1987 os << "nullptr" << "\n";
1988 }
1989 os << "_rearrangedDen" << "\n";
1990 if(_rearrangedDen) {
1991 os << getColonSeparatedNameString(*_rearrangedDen) << "\n";
1992 } else {
1993 os << "nullptr" << "\n";
1994 }
1995}
1996
1997std::unique_ptr<RooArgSet> RooProdPdf::fillNormSetForServer(RooArgSet const &normSet, RooAbsArg const &server) const
1998{
1999 if (normSet.empty())
2000 return nullptr;
2001 auto *pdfNset = findPdfNSet(static_cast<RooAbsPdf const &>(server));
2002 if (pdfNset && !pdfNset->empty()) {
2003 std::unique_ptr<RooArgSet> out;
2004 if (0 == strcmp("cset", pdfNset->GetName())) {
2005 // If the name of the normalization set is "cset", it doesn't contain the
2006 // normalization set but the conditional observables that should *not* be
2007 // normalized over.
2008 out = std::make_unique<RooArgSet>(normSet);
2009 RooArgSet common;
2010 out->selectCommon(*pdfNset, common);
2011 out->remove(common);
2012 } else {
2013 out = std::make_unique<RooArgSet>(*pdfNset);
2014 }
2015 // prefix also the arguments in the normSets if they have not already been
2016 if (auto prefix = getStringAttribute("__prefix__")) {
2017 for (RooAbsArg *arg : *out) {
2018 if (!arg->getStringAttribute("__prefix__")) {
2019 arg->SetName((std::string(prefix) + arg->GetName()).c_str());
2020 arg->setStringAttribute("__prefix__", prefix);
2021 }
2022 }
2023 }
2024 return out;
2025 } else {
2026 return nullptr;
2027 }
2028}
2029
2030std::unique_ptr<RooAbsArg>
2032{
2033 if (ctx.likelihoodMode()) {
2034 auto binnedInfo = RooHelpers::getBinnedL(*this);
2035 if (binnedInfo.binnedPdf && binnedInfo.binnedPdf != this) {
2036 return binnedInfo.binnedPdf->compileForNormSet(normSet, ctx);
2037 }
2038 }
2039
2040 std::unique_ptr<RooProdPdf> prodPdfClone{static_cast<RooProdPdf *>(this->Clone())};
2041 ctx.markAsCompiled(*prodPdfClone);
2042
2043 // If this RooProdPdf has a normalization range (e.g. the fit range in a
2044 // ranged fit), propagate it to the component pdfs while they are compiled,
2045 // so that a nested RooAddPdf reinterprets its coefficients with respect to
2046 // the full range exactly like a top-level RooAddPdf would. Otherwise, a
2047 // RooProdPdf wrapping an extended RooAddPdf (the common way to attach
2048 // constraint terms) gives a different yield than the bare RooAddPdf in a
2049 // ranged fit (GitHub issue #16673).
2050 //
2051 // This must not change how constraint terms are normalized, so the range is
2052 // only set on components whose normalization observables define it.
2053 // Constraint pdfs are normalized over the nuisance parameters, which don't
2054 // define the fit range, so they are skipped. Forcing the range on them would
2055 // be wrong, and for a multi-range fit it even throws, because each undefined
2056 // sub-range falls back to the parameter's full range and the sub-ranges then
2057 // overlap.
2058 std::vector<std::pair<RooAbsPdf *, std::string>> restoreNormRanges;
2059 if (const char *prodNormRange = normRange()) {
2060 std::vector<std::string> rangeTokens = ROOT::Split(prodNormRange, ",", /*skipEmpty=*/true);
2061 for (auto *pdf : static_range_cast<RooAbsPdf *>(prodPdfClone->_pdfList)) {
2062 RooArgSet pdfObs;
2063 pdf->getObservables(&normSet, pdfObs);
2064 bool rangeDefined = !pdfObs.empty();
2065 for (RooAbsArg *obs : pdfObs) {
2066 auto *lval = dynamic_cast<RooAbsRealLValue *>(obs);
2067 for (auto const &token : rangeTokens) {
2068 if (!lval || !lval->hasRange(token.c_str())) {
2069 rangeDefined = false;
2070 }
2071 }
2072 }
2073 if (rangeDefined && std::string(pdf->normRange() ? pdf->normRange() : "") != prodNormRange) {
2074 restoreNormRanges.emplace_back(pdf, pdf->normRange() ? pdf->normRange() : "");
2075 pdf->setNormRange(prodNormRange);
2076 }
2077 }
2078 }
2079
2080 // Pdfs with overlapping normalization observables end up in the same term,
2081 // which a RooGenProdProj normalizes as a whole. Like in the scalar
2082 // evaluation, it should integrate the unnormalized pdfs. The RooGenProdProj
2083 // clones its pdfs, and the clone of a RooNormalizedPdf owns a copy of the
2084 // normalization integral that the RooFit::Evaluator doesn't keep up to
2085 // date. Also, the integrals of normalized pdfs are smaller, so the absolute
2086 // tolerance of the numerical integration costs more precision.
2087 std::unordered_set<std::string> jointlyNormalized;
2088 if (!normSet.empty()) {
2089 Factorized factorized;
2090 prodPdfClone->factorizeProduct(normSet, RooArgSet{}, factorized);
2091 for (auto *term : static_range_cast<RooArgSet *>(factorized.terms)) {
2092 if (term->size() > 1) {
2093 for (RooAbsArg *pdf : *term) {
2094 jointlyNormalized.insert(pdf->GetName());
2095 }
2096 }
2097 }
2098 }
2099
2100 for (const auto server : prodPdfClone->servers()) {
2101 auto nsetForServer = fillNormSetForServer(normSet, *server);
2102 RooArgSet const &nset = nsetForServer ? *nsetForServer : normSet;
2103
2104 RooArgSet depList;
2105 server->getObservables(&nset, depList);
2106
2107 ctx.compileServer(*server, *prodPdfClone, depList);
2108 }
2109
2110 for (auto const &[pdf, oldRange] : restoreNormRanges) {
2111 pdf->setNormRange(oldRange.empty() ? nullptr : oldRange.c_str());
2112 }
2113
2114 // The computation graph is created from a shallow clone with the jointly
2115 // normalized pdfs unwrapped. The RooFixedProdPdf forwards analytical
2116 // integrals to prodPdfClone, which keeps them wrapped so that these
2117 // integrals match the normalized value, e.g. in a nested RooProdPdf.
2118 std::unordered_map<RooAbsArg *, RooAbsArg *> unwrapped;
2119 for (RooAbsArg *pdf : prodPdfClone->_pdfList) {
2120 auto *normalized = dynamic_cast<RooFit::Detail::RooNormalizedPdf *>(pdf);
2121 if (normalized && jointlyNormalized.count(normalized->pdf().GetName())) {
2122 unwrapped[normalized] = const_cast<RooAbsPdf *>(&normalized->pdf());
2123 }
2124 }
2125 std::unique_ptr<RooProdPdf> cacheProdPdf;
2126 if (!unwrapped.empty()) {
2127 cacheProdPdf.reset(static_cast<RooProdPdf *>(prodPdfClone->Clone()));
2128 cacheProdPdf->redirectServers(unwrapped);
2129 }
2130
2131 // The RooFixedProdPdf keeps the normalization set around to create its
2132 // cache elements and to evaluate the expected number of events. It must
2133 // refer to the observables in the compiled computation graph, because the
2134 // original ones can get out of sync with it: RooSimultaneous prefixes the
2135 // observable names of each channel after compilation, and the stale
2136 // observables would then not be found anymore by name.
2137 auto fixedProdPdf = std::make_unique<RooFit::Detail::RooFixedProdPdf>(
2138 std::move(prodPdfClone), ctx.mapToCompiled(normSet), cacheProdPdf.get());
2139 ctx.markAsCompiled(*fixedProdPdf);
2140
2141 return fixedProdPdf;
2142}
2143
2144namespace RooFit::Detail {
2145
2146/// \param[in] prodPdf The product with the fixed normalization set.
2147/// \param[in] normSet The normalization set.
2148/// \param[in] cacheProdPdf If not null, the cache elements that make up the
2149/// computation graph are created from this product instead of
2150/// `prodPdf`. It must have the same observables and the same pdfs,
2151/// but they may be normalized differently.
2152RooFixedProdPdf::RooFixedProdPdf(std::unique_ptr<RooProdPdf> &&prodPdf, RooArgSet const &normSet,
2153 RooProdPdf const *cacheProdPdf)
2154 : RooAbsPdf(prodPdf->GetName(), prodPdf->GetTitle()),
2155 _normSet{normSet},
2156 _servers("!servers", "List of servers", this),
2157 _prodPdf{std::move(prodPdf)}
2158{
2159 auto cache = (cacheProdPdf ? cacheProdPdf : _prodPdf.get())->createCacheElem(&_normSet, nullptr);
2160 _isRearranged = cache->_isRearranged;
2161
2162 // We don't want to carry the full cache object around, so we let it go out
2163 // of scope and transfer the ownership of the args that we actually need.
2164 cache->_ownedList.releaseOwnership();
2165 cache->_numList.releaseOwnership();
2166 cache->_denList.releaseOwnership();
2167 addOwnedComponents(cache->_ownedList);
2168 addOwnedComponents(cache->_numList);
2169 addOwnedComponents(cache->_denList);
2170
2171 // The actual servers for a given normalization set depend on whether the
2172 // cache is rearranged or not. See RooProdPdf::calculate to see
2173 // which args in the cache are used directly.
2174 if (_isRearranged) {
2175 _servers.add(*cache->_rearrangedNum);
2176 _servers.add(*cache->_rearrangedDen);
2177 addOwnedComponents(std::move(cache->_rearrangedNum));
2178 addOwnedComponents(std::move(cache->_rearrangedDen));
2179 return;
2180 }
2181 for (RooAbsArg *arg : cache->_partList) {
2182 _servers.add(*arg);
2183 }
2184}
2185
2187 : RooAbsPdf(other, name),
2188 _normSet{other._normSet},
2189 _servers("!servers", this, other._servers),
2190 _prodPdf{static_cast<RooProdPdf *>(other._prodPdf->Clone())},
2191 _isRearranged{other._isRearranged}
2192{
2193}
2194
2195////////////////////////////////////////////////////////////////////////////////
2196/// Evaluate product of PDFs in batch mode.
2197
2199{
2200 if (_isRearranged) {
2201 auto numerator = ctx.at(rearrangedNum());
2202 auto denominator = ctx.at(rearrangedDen());
2203 RooBatchCompute::compute(ctx.config(this), RooBatchCompute::Ratio, ctx.output(), {numerator, denominator});
2204 return;
2205 }
2206 std::vector<std::span<const double>> factors;
2207 factors.reserve(partList()->size());
2208 for (const RooAbsArg *arg : *partList()) {
2209 auto span = ctx.at(arg);
2210 factors.push_back(span);
2211 }
2212 std::array<double, 1> special{static_cast<double>(factors.size())};
2213 RooBatchCompute::compute(ctx.config(this), RooBatchCompute::ProdPdf, ctx.output(), factors, special);
2214}
2215
2217{
2218 if (_isRearranged) {
2219 return rearrangedNum()->getVal() / rearrangedDen()->getVal();
2220 }
2221 double value = 1.0;
2222
2223 for (auto *arg : static_range_cast<RooAbsReal *>(*partList())) {
2224 value *= arg->getVal();
2225 }
2226 return value;
2227}
2228
2229} // namespace RooFit::Detail
ROOT::RRangeCast< T, false, Range_t > static_range_cast(Range_t &&coll)
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
#define cxcoutD(a)
#define coutW(a)
#define dologD(a)
#define coutF(a)
#define coutE(a)
static void indent(ostringstream &buf, int indent_level)
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void input
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
char name[80]
Definition TGX11.cxx:142
char * Form(const char *fmt,...)
Formats a string in a circular formatting buffer.
Definition TString.cxx:2571
Common abstract base class for objects that represent a value and a "shape" in RooFit.
Definition RooAbsArg.h:77
RooExpensiveObjectCache & expensiveObjectCache() const
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...
bool addOwnedComponents(const RooAbsCollection &comps)
Take ownership of the contents of 'comps'.
const Text_t * getStringAttribute(const Text_t *key) const
Get string attribute mapped under key 'key'.
bool redirectServers(const RooAbsCollection &newServerList, bool mustReplaceAll=false, bool nameChange=false, bool isRecursionStep=false)
Replace all direct servers of this object with the new servers in newServerList.
friend class RooRealIntegral
Definition RooAbsArg.h:544
RooFit::OwningPtr< RooArgSet > getComponents() const
Create a RooArgSet with all components (branch nodes) of the expression tree headed by this object.
TObject * Clone(const char *newname=nullptr) const override
Make a clone of an object using the Streamer facility.
Definition RooAbsArg.h:89
void treeNodeServerList(RooAbsCollection *list, const RooAbsArg *arg=nullptr, bool doBranch=true, bool doLeaf=true, bool valueOnly=false, bool recurseNonDerived=false) const
Fill supplied list with nodes of the arg tree, following all server links, starting with ourself as t...
RooAbsArg * findNewServer(const RooAbsCollection &newSet, bool nameChange) const
Find the new server in the specified set that matches the old server.
OperMode operMode() const
Query the operation mode of this node.
Definition RooAbsArg.h:399
Abstract container object that can hold multiple RooAbsArg objects.
bool contains(const char *name) const
Check if collection contains an argument with a specific name.
virtual bool remove(const RooAbsArg &var, bool silent=false, bool matchByNameOnly=false)
Remove the specified argument from our list.
virtual bool add(const RooAbsArg &var, bool silent=false)
Add the specified argument to list.
Int_t index(const RooAbsArg *arg) const
Returns index of given arg, or -1 if arg is not in the collection.
bool overlaps(Iterator_t otherCollBegin, Iterator_t otherCollEnd) const
Storage_t::size_type size() const
const_iterator begin() const
RooAbsArg * find(const char *name) const
Find object with given name in list.
Abstract base class for generator contexts of RooAbsPdf objects.
Abstract interface for all probability density functions.
Definition RooAbsPdf.h:32
TString _normRange
Normalization range.
Definition RooAbsPdf.h:336
virtual bool isDirectGenSafe(const RooAbsArg &arg) const
Check if given observable can be safely generated using the pdfs internal generator mechanism (if tha...
RooArgSet const * _normSet
! Normalization set with for above integral
Definition RooAbsPdf.h:314
bool canBeExtended() const
If true, PDF can provide extended likelihood term.
Definition RooAbsPdf.h:214
@ CanNotBeExtended
Definition RooAbsPdf.h:208
const char * normRange() const
Definition RooAbsPdf.h:246
bool redirectServersHook(const RooAbsCollection &newServerList, bool mustReplaceAll, bool nameChange, bool isRecursiveStep) override
Hook function intercepting redirectServer calls.
virtual RooAbsGenContext * genContext(const RooArgSet &vars, const RooDataSet *prototype=nullptr, const RooArgSet *auxProto=nullptr, bool verbose=false) const
Interface function to create a generator context from a p.d.f.
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
double getVal(const RooArgSet *normalisationSet=nullptr) const
Evaluate object.
Definition RooAbsReal.h:107
bool _forceNumInt
Force numerical integration if flag set.
Definition RooAbsReal.h:544
TString integralNameSuffix(const RooArgSet &iset, const RooArgSet *nset=nullptr, const char *rangeName=nullptr, bool omitEmpty=false) const
Construct string with unique suffix name to give to integral object that encodes integrated observabl...
RooFit::OwningPtr< RooAbsReal > createIntegral(const RooArgSet &iset, const RooCmdArg &arg1, const RooCmdArg &arg2={}, const RooCmdArg &arg3={}, const RooCmdArg &arg4={}, const RooCmdArg &arg5={}, const RooCmdArg &arg6={}, const RooCmdArg &arg7={}, const RooCmdArg &arg8={}) const
Create an object that represents the integral of the function over one or more observables listed in ...
Calculates the sum of a set of RooAbsReal terms, or when constructed with two sets,...
Definition RooAddition.h:27
static TClass * Class()
RooArgList is a container object that can hold multiple RooAbsArg objects.
Definition RooArgList.h:22
RooAbsArg * at(Int_t idx) const
Return object at given index, or nullptr if index is out of range.
Definition RooArgList.h:110
RooArgSet is a container object that can hold multiple RooAbsArg objects.
Definition RooArgSet.h:24
RooArgSet * snapshot(bool deepCopy=true) const
Use RooAbsCollection::snapshot(), but return as RooArgSet.
Definition RooArgSet.h:159
RooArgSet * selectCommon(const RooAbsCollection &refColl) const
Use RooAbsCollection::selecCommon(), but return as RooArgSet.
Definition RooArgSet.h:154
Int_t setObj(const RooArgSet *nset, T *obj, const TNamed *isetRangeName=nullptr)
Setter function without integration set.
RooArgSet selectFromSet1(RooArgSet const &argSet, int index) const
Create RooArgSet containing the objects that are both in the cached set 1 with a given index and an i...
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...
void reset()
Clear the cache.
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.
Named container for two doubles, two integers two object points and three string pointers that can be...
Definition RooCmdArg.h:26
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...
bool remove(const RooAbsArg &var, bool silent=false, bool matchByNameOnly=false) override
Remove object 'var' from set and deregister 'var' as server to owner.
TObject * clone(const char *newname=nullptr) const override
Definition RooConstVar.h:29
RooCustomizer is a factory class to produce clones of a prototype composite PDF object with the same ...
Container class to hold unbinned data.
Definition RooDataSet.h:32
void markAsCompiled(RooAbsArg &arg) const
A RooProdPdf with a fixed normalization set can be replaced by this class.
Definition RooProdPdf.h:209
RooArgSet const * partList() const
Definition RooProdPdf.h:265
double evaluate() const override
Evaluate this PDF / function / constant. Needs to be overridden by all derived classes.
void doEval(RooFit::EvalContext &ctx) const override
Evaluate product of PDFs in batch mode.
std::unique_ptr< RooProdPdf > _prodPdf
Definition RooProdPdf.h:272
RooAbsReal const * rearrangedDen() const
Definition RooProdPdf.h:260
RooFixedProdPdf(std::unique_ptr< RooProdPdf > &&prodPdf, RooArgSet const &normSet, RooProdPdf const *cacheProdPdf=nullptr)
RooAbsReal const * rearrangedNum() const
Definition RooProdPdf.h:256
A RooFormulaVar is a generic implementation of a real-valued object, which takes a RooArgList of serv...
Collection class for internal use, storing a collection of RooAbsArg pointers in a doubly linked list...
void Delete(Option_t *o=nullptr) override
Remove all elements in collection and delete all elements NB: Collection does not own elements,...
static const char * str(const TNamed *ptr)
Return C++ string corresponding to given TNamed pointer.
Definition RooNameReg.h:39
static const TNamed * ptr(const char *stringPtr)
Return a unique TNamed pointer for given C++ string.
RooArgList containedArgs(Action) override
Return RooAbsArg components contained in the cache.
std::unique_ptr< RooAbsReal > _rearrangedNum
Definition RooProdPdf.h:115
void printCompactTreeHook(std::ostream &, const char *, Int_t, Int_t) override
Hook function to print cache contents in tree printing of RooProdPdf.
std::vector< std::unique_ptr< RooArgSet > > _normList
Definition RooProdPdf.h:113
std::unique_ptr< RooAbsReal > _rearrangedDen
Definition RooProdPdf.h:116
void writeToStream(std::ostream &os) const
Efficient implementation of a product of PDFs of the form.
Definition RooProdPdf.h:35
std::unique_ptr< RooArgSet > getConstraints(const RooArgSet &observables, RooArgSet const &constrainedParams, RooArgSet &pdfParams) const override
Return all parameter constraint p.d.f.s on parameters listed in constrainedParams.
Int_t getGenerator(const RooArgSet &directVars, RooArgSet &generateVars, bool staticInitOK=true) const override
Query internal generation capabilities of component p.d.f.s and aggregate capabilities into master co...
void rearrangeProduct(CacheElem &) const
double analyticalIntegralWN(Int_t code, const RooArgSet *normSet, const char *rangeName=nullptr) const override
Return analytical integral defined by given scenario code.
~RooProdPdf() override
Destructor.
Int_t _extendedIndex
Index of extended PDF (if any)
Definition RooProdPdf.h:190
std::unique_ptr< RooAbsReal > specializeRatio(RooFormulaVar &input, const char *targetRangeName) const
RooProdPdf()
Default constructor.
void removePdfs(RooAbsCollection const &pdfs)
Remove some PDFs from the factors of this RooProdPdf.
bool _useDefaultGen
Use default or distributed event generator.
Definition RooProdPdf.h:193
std::vector< std::unique_ptr< RooArgSet > > _pdfNSetList
List of PDF component normalization sets.
Definition RooProdPdf.h:189
std::unique_ptr< RooArgSet > fillNormSetForServer(RooArgSet const &normSet, RooAbsArg const &server) const
std::unique_ptr< RooAbsReal > specializeIntegral(RooAbsReal &orig, const char *targetRangeName) const
void factorizeProduct(const RooArgSet &normSet, const RooArgSet &intSet, Factorized &factorized) const
Factorize product in irreducible terms for given choice of integration/normalization.
bool forceAnalyticalInt(const RooAbsArg &dep) const override
Force RooRealIntegral to offer all observables for internal integration.
std::unique_ptr< RooAbsArg > compileForNormSet(RooArgSet const &normSet, RooFit::Detail::CompileContext &ctx) const override
RooAbsGenContext * genContext(const RooArgSet &vars, const RooDataSet *prototype=nullptr, const RooArgSet *auxProto=nullptr, bool verbose=false) const override
Return generator context optimized for generating events from product p.d.f.s.
TNamed * _refRangeName
Reference range name for interpretation of conditional products.
Definition RooProdPdf.h:195
void addPdfs(RooAbsCollection const &pdfs)
Add some full PDFs to the factors of this RooProdPdf.
RooListProxy _pdfList
List of PDF components.
Definition RooProdPdf.h:188
Int_t getPartIntList(const RooArgSet *nset, const RooArgSet *iset, const char *isetRangeName=nullptr) const
Return list of (partial) integrals of product terms for integration of p.d.f over observables iset wh...
void printMetaArgs(std::ostream &os) const override
Customized printing of arguments of a RooProdPdf to more intuitively reflect the contents of the prod...
std::list< double > * binBoundaries(RooAbsRealLValue &, double, double) const override
Forward the plot sampling hint from the p.d.f. that defines the observable obs.
RooObjCacheManager _cacheMgr
! The cache manager
Definition RooProdPdf.h:171
std::string makeRGPPName(const char *pfx, const RooArgSet &term, const RooArgSet &iset, const RooArgSet &nset, const char *isetRangeName) const
Make an appropriate automatic name for a RooGenProdProj object in getPartIntList()
bool isDirectGenSafe(const RooAbsArg &arg) const override
Forward determination of safety of internal generator code to component p.d.f that would generate the...
ProcessProductTermOutput processProductTerm(const RooArgSet *nset, const RooArgSet *iset, const char *isetRangeName, const RooArgSet *term, const RooArgSet &termNSet, const RooArgSet &termISet, bool forceWrap=false) const
Calculate integrals of factorized product terms over observables iset while normalized to observables...
std::unique_ptr< RooAbsReal > makeCondPdfRatioCorr(RooAbsReal &term, const RooArgSet &termNset, const RooArgSet &termImpSet, const char *normRange, const char *refRange) const
For single normalization ranges.
RooArgSet * findPdfNSet(RooAbsPdf const &pdf) const
Look up user specified normalization set for given input PDF component.
ExtendMode extendMode() const override
If this product contains exactly one extendable p.d.f return the extension abilities of that p....
std::list< double > * plotSamplingHint(RooAbsRealLValue &obs, double xlo, double xhi) const override
Forward the plot sampling hint from the p.d.f. that defines the observable obs.
double expectedEvents(const RooArgSet *nset) const override
Return the expected number of events associated with the extendable input PDF in the product.
Int_t getAnalyticalIntegralWN(RooArgSet &allVars, RooArgSet &numVars, const RooArgSet *normSet, const char *rangeName=nullptr) const override
Determine which part (if any) of given integral can be performed analytically.
bool isBinnedDistribution(const RooArgSet &obs) const override
If all components that depend on obs are binned that so is the product.
friend class RooProdGenContext
Definition RooProdPdf.h:179
RooArgSet * getConnectedParameters(const RooArgSet &observables) const
Return all parameter constraint p.d.f.s on parameters listed in constrainedParams.
double calculate(const RooProdPdf::CacheElem &cache, bool verbose=false) const
Calculate running product of pdfs terms, using the supplied normalization set in 'normSetList' for ea...
RooArgSet _defNormSet
Default normalization set.
Definition RooProdPdf.h:198
std::unique_ptr< RooAbsReal > createExpectedEventsFunc(const RooArgSet *nset) const override
Returns an object that represents the expected number of events for a given normalization set,...
CacheElem * getCacheElem(RooArgSet const *nset) const
bool redirectServersHook(const RooAbsCollection &, bool, bool, bool) override
Implement support for node removal.
void groupProductTerms(std::list< std::vector< RooArgSet * > > &groupedTerms, RooArgSet &outerIntDeps, Factorized const &factorized) const
Group product into terms that can be calculated independently.
std::vector< std::vector< int > > _genCode
! Registry of composite direct generator codes
Definition RooProdPdf.h:185
void initGenerator(Int_t code) override
Forward one-time initialization call to component generation initialization methods.
void generateEvent(Int_t code) override
Generate a single event with configuration specified by 'code' Defer internal generation to component...
void fixRefRange(const char *rangeName)
std::unique_ptr< CacheElem > createCacheElem(const RooArgSet *nset, const RooArgSet *iset, const char *isetRangeName=nullptr) const
double evaluate() const override
Calculate current value of object.
void initializeFromCmdArgList(const RooArgSet &fullPdfSet, const RooLinkedList &l)
Initialize RooProdPdf configuration from given list of RooCmdArg configuration arguments and set of '...
void selectNormalizationRange(const char *rangeName=nullptr, bool force=false) override
Interface function used by test statistics to freeze choice of range for interpretation of conditiona...
double _cutOff
Cutoff parameter for running product.
Definition RooProdPdf.h:187
Represents the product of a given set of RooAbsReal objects.
Definition RooProduct.h:29
static TClass * Class()
Performs hybrid numerical/analytical integrals of RooAbsReal objects.
RooFit::OwningPtr< RooAbsReal > createIntegral(const RooArgSet &iset, const RooArgSet *nset=nullptr, const RooNumIntConfig *cfg=nullptr, const char *rangeName=nullptr) const override
Create an object that represents the integral of the function over one or more observables listed in ...
const RooAbsReal & integrand() const
static TClass * Class()
Variable that can be changed from the outside.
Definition RooRealVar.h:37
The TNamed class is the base class for all named ROOT classes.
Definition TNamed.h:29
const char * GetName() const override
Returns name of object.
Definition TNamed.h:49
Mother of all ROOT objects.
Definition TObject.h:42
virtual Bool_t InheritsFrom(const char *classname) const
Returns kTRUE if object inherits from class "classname".
Definition TObject.cxx:548
Basic string class.
Definition TString.h:137
Ssiz_t Length() const
Definition TString.h:426
const char * Data() const
Definition TString.h:385
Bool_t Contains(const char *pat, ECaseCompare cmp=kExact) const
Definition TString.h:642
RooConstVar & RooConst(double val)
Double_t x[n]
Definition legend1.C:17
std::vector< std::string > Split(std::string_view str, std::string_view delims, bool skipEmpty=false)
Splits a string at each character in delims.
void compute(Config cfg, Computer comp, std::span< double > output, VarSpan vars, ArgSpan extraArgs={})
BinnedLOutput getBinnedL(RooAbsPdf const &pdf)
std::string getColonSeparatedNameString(RooArgSet const &argSet, char delim=':')
RooLinkedList cross
Definition RooProdPdf.h:145
RooLinkedList norms
Definition RooProdPdf.h:142
RooLinkedList terms
Definition RooProdPdf.h:141
TLine l
Definition textangle.C:4