forked from root-project/root
-
Notifications
You must be signed in to change notification settings - Fork 1
/
RooGenProdProj.cxx
275 lines (217 loc) · 10.4 KB
/
RooGenProdProj.cxx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
/*****************************************************************************
* Project: RooFit *
* Package: RooFitCore *
* @(#)root/roofitcore:$Id$
* Authors: *
* WV, Wouter Verkerke, UC Santa Barbara, verkerke@slac.stanford.edu *
* DK, David Kirkby, UC Irvine, dkirkby@uci.edu *
* *
* Copyright (c) 2000-2005, Regents of the University of California *
* and Stanford University. All rights reserved. *
* *
* Redistribution and use in source and binary forms, *
* with or without modification, are permitted according to the terms *
* listed in LICENSE (http://roofit.sourceforge.net/license.txt) *
*****************************************************************************/
/**
\file RooGenProdProj.cxx
\class RooGenProdProj
\ingroup Roofitcore
RooGenProdProj is an auxiliary class for RooProdPdf that calculates
a general normalised projection of a product of non-factorising PDFs, e.g.
\f[
P_{x,xy} = \frac{\int ( P1 * P2 * \ldots) \mathrm{d}x}{\int ( P1 * P2 * \ldots ) \mathrm{d}x \mathrm{d}y}
\f]
Partial integrals, which factorise and can be calculated, are calculated
analytically. Remaining non-factorising observables are integrated numerically.
**/
#include "Riostream.h"
#include <cmath>
#include "RooGenProdProj.h"
#include "RooAbsReal.h"
#include "RooAbsPdf.h"
#include "RooErrorHandler.h"
#include "RooProduct.h"
////////////////////////////////////////////////////////////////////////////////
/// Constructor for a normalization projection of the product of p.d.f.s _prodSet
/// integrated over _intSet in range isetRangeName while normalized over _normSet
RooGenProdProj::RooGenProdProj(const char *name, const char *title, const RooArgSet& _prodSet, const RooArgSet& _intSet,
const RooArgSet& _normSet, const char* isetRangeName, const char* normRangeName, bool doFactorize) :
RooAbsReal(name, title),
_compSetN("compSetN","Set of integral components owned by numerator",this,false),
_compSetD("compSetD","Set of integral components owned by denominator",this,false),
_intList("intList","List of integrals",this,true)
{
// Set expensive object cache to that of first item in prodSet
setExpensiveObjectCache(_prodSet.first()->expensiveObjectCache()) ;
// Create owners of components created in ctor
_compSetOwnedN = std::make_unique<RooArgSet>();
_compSetOwnedD = std::make_unique<RooArgSet>();
RooAbsReal* numerator = makeIntegral("numerator",_prodSet,_intSet,*_compSetOwnedN,isetRangeName,doFactorize) ;
RooAbsReal* denominator = makeIntegral("denominator",_prodSet,_normSet,*_compSetOwnedD,normRangeName,doFactorize) ;
// cout << "RooGenProdPdf::ctor(" << GetName() << ") numerator = " << numerator->GetName() << endl ;
// numerator->printComponentTree() ;
// cout << "RooGenProdPdf::ctor(" << GetName() << ") denominator = " << denominator->GetName() << endl ;
// denominator->printComponentTree() ;
// Copy all components in (non-owning) set proxy
_compSetN.add(*_compSetOwnedN) ;
_compSetD.add(*_compSetOwnedD) ;
_intList.add(*numerator) ;
if (denominator) {
_intList.add(*denominator) ;
_haveD = true ;
}
}
////////////////////////////////////////////////////////////////////////////////
/// Copy constructor
RooGenProdProj::RooGenProdProj(const RooGenProdProj &other, const char *name)
: RooAbsReal(other, name),
_compSetN("compSetN", "Set of integral components owned by numerator", this),
_compSetD("compSetD", "Set of integral components owned by denominator", this),
_intList("intList", "List of integrals", this),
_haveD(other._haveD)
{
// Copy constructor
_compSetOwnedN = std::make_unique<RooArgSet>();
other._compSetN.snapshot(*_compSetOwnedN);
_compSetN.add(*_compSetOwnedN) ;
_compSetOwnedD = std::make_unique<RooArgSet>();
other._compSetD.snapshot(*_compSetOwnedD);
_compSetD.add(*_compSetOwnedD) ;
for (RooAbsArg * arg : *_compSetOwnedN) {
arg->setOperMode(_operMode) ;
}
for (RooAbsArg * arg : *_compSetOwnedD) {
arg->setOperMode(_operMode) ;
}
// Fill _intList
_intList.add(*_compSetN.find(other._intList.at(0)->GetName())) ;
if (other._haveD) {
_intList.add(*_compSetD.find(other._intList.at(1)->GetName())) ;
}
}
////////////////////////////////////////////////////////////////////////////////
/// Utility function to create integral for product over certain observables.
/// \param[in] name Name of integral to be created.
/// \param[in] compSet All components of the product.
/// \param[in] intSet Observables to be integrated.
/// \param[out] saveSet All component objects needed to represent the product integral are added as owned members to saveSet.
/// \note The set owns new components that are created for the integral.
/// \param[in] isetRangeName Integral range.
/// \param[in] doFactorize
///
/// \return A RooAbsReal object representing the requested integral. The object is owned by `saveSet`.
///
/// The integration is factorized into components as much as possible and done analytically as far as possible.
RooAbsReal* RooGenProdProj::makeIntegral(const char* name, const RooArgSet& compSet, const RooArgSet& intSet,
RooArgSet& saveSet, const char* isetRangeName, bool doFactorize)
{
RooArgSet anaIntSet;
RooArgSet numIntSet;
// First determine subset of observables in intSet that are factorizable
for (const auto arg : intSet) {
auto count = std::count_if(compSet.begin(), compSet.end(), [arg](const RooAbsArg* pdfAsArg){
auto pdf = static_cast<const RooAbsPdf*>(pdfAsArg);
return (pdf->dependsOn(*arg));
});
if (count==1) {
anaIntSet.add(*arg) ;
}
}
// Determine which of the factorizable integrals can be done analytically
RooArgSet prodSet ;
numIntSet.add(intSet) ;
// The idea of the RooGenProdProj is that we divide two integral objects each
// created with this makeIntegral() function to get the normalized integral of
// a product. Therefore, we don't need to normalize the numerater and
// denominator integrals themselves. Doing the normalization would be
// expensive and it would cancel out anyway. However, if we don't specify an
// explicit normalization integral in createIntegral(), the last-used
// normalization set might be used to normalize the pdf, resulting in
// redundant computations.
//
// For this reason, the normalization set of the integrated pdfs is fixed to
// an empty set in this case. Note that in RooFit, a nullptr normalization
// set and an empty normalization set is not equivalent. The former implies
// taking the last-used normalization set, and the latter means explicitly no
// normalization.
RooArgSet emptyNormSet{};
RooArgSet keepAlive;
for (const auto pdfAsArg : compSet) {
auto pdf = static_cast<const RooAbsPdf*>(pdfAsArg);
if (doFactorize && pdf->dependsOn(anaIntSet)) {
RooArgSet anaSet ;
Int_t code = pdf->getAnalyticalIntegralWN(anaIntSet,anaSet,nullptr,isetRangeName) ;
if (code!=0) {
// Analytical integral, create integral object
std::unique_ptr<RooAbsReal> pai{pdf->createIntegral(anaSet,emptyNormSet,isetRangeName)};
pai->setOperMode(_operMode) ;
// Add to integral to product
prodSet.add(*pai) ;
// Remove analytically integratable observables from numeric integration list
numIntSet.remove(anaSet) ;
// Keep integral alive until the prodSet is cloned later
keepAlive.addOwned(std::move(pai));
} else {
// Analytic integration of factorizable observable not possible, add straight pdf to product
prodSet.add(*pdf) ;
}
} else {
// Non-factorizable observables, add straight pdf to product
prodSet.add(*pdf) ;
}
}
// Create product of (partial) analytical integrals
TString prodName ;
if (isetRangeName) {
prodName = Form("%s_%s_Range[%s]",GetName(),name,isetRangeName) ;
} else {
prodName = Form("%s_%s",GetName(),name) ;
}
// Create clones of the elements in prodSet. These need to be cloned
// because when caching optimisation lvl 2 is activated, pre-computed
// values are side-loaded into the elements.
// Those pre-cached values already contain normalisation constants, so
// the integral comes out wrongly. Therefore, we create here nodes that
// don't participate in any caching, which are used to compute integrals.
RooArgSet prodSetClone;
prodSet.snapshot(prodSetClone, false);
auto prod = std::make_unique<RooProduct>(prodName, "product", prodSetClone);
prod->setExpensiveObjectCache(expensiveObjectCache()) ;
prod->setOperMode(_operMode) ;
// Create integral performing remaining numeric integration over (partial) analytic product
std::unique_ptr<RooAbsReal> integral{prod->createIntegral(numIntSet,emptyNormSet,isetRangeName)};
integral->setOperMode(_operMode) ;
auto ret = integral.get();
// Declare ownership of prodSet, product, and integral
saveSet.addOwned(std::move(prodSetClone));
saveSet.addOwned(std::move(prod));
saveSet.addOwned(std::move(integral)) ;
// Caller owners returned master integral object
return ret ;
}
////////////////////////////////////////////////////////////////////////////////
/// Calculate and return value of normalization projection
double RooGenProdProj::evaluate() const
{
RooArgSet const* nset = _intList.nset();
double nom = static_cast<RooAbsReal*>(_intList.at(0))->getVal(nset);
if (!_haveD) return nom ;
double den = static_cast<RooAbsReal*>(_intList.at(1))->getVal(nset);
//cout << "RooGenProdProj::eval(" << GetName() << ") nom = " << nom << " den = " << den << endl ;
return nom / den ;
}
////////////////////////////////////////////////////////////////////////////////
/// Intercept cache mode operation changes and propagate them to the components
void RooGenProdProj::operModeHook()
{
// WVE use cache manager here!
for(RooAbsArg * arg : *_compSetOwnedN) {
arg->setOperMode(_operMode) ;
}
for(RooAbsArg * arg : *_compSetOwnedD) {
arg->setOperMode(_operMode) ;
}
_intList.at(0)->setOperMode(_operMode) ;
if (_haveD) _intList.at(1)->setOperMode(Auto) ; // Denominator always stays in Auto mode (normalization integral)
}