Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RooIntegralMorph.cxx
Go to the documentation of this file.
1/*****************************************************************************
2 * Project: RooFit *
3 * *
4 * Copyright (c) 2000-2005, Regents of the University of California *
5 * and Stanford University. All rights reserved. *
6 * *
7 * Redistribution and use in source and binary forms, *
8 * with or without modification, are permitted according to the terms *
9 * listed in LICENSE (http://roofit.sourceforge.net/license.txt) *
10 *****************************************************************************/
11
12/** \class RooIntegralMorph
13 \ingroup Roofit
14
15Class RooIntegralMorph is an implementation of the histogram interpolation
16technique described by Alex Read in 'NIM A 425 (1999) 357-369 'Linear interpolation of histograms'
17for continuous functions rather than histograms. The interpolation method, in short,
18works as follows.
19
20 - Given a p.d.f f1(x) with c.d.f F1(x) and p.d.f f2(x) with c.d.f F2(x)
21
22 - One finds takes a value 'y' of both c.d.fs and determines the corresponding x
23 values x(1,2) at which F(1,2)(x)==y.
24
25 - The value of the interpolated p.d.f fbar(x) is then calculated as
26 fbar(alpha*x1+(1-alpha)*x2) = f1(x1)*f2(x2) / ( alpha*f2(x2) + (1-alpha)*f1(x1) ) ;
27
28From a technical point of view class RooIntegralMorph is a p.d.f that takes
29two input p.d.fs f1(x,p) an f2(x,q) and an interpolation parameter to
30make a p.d.f fbar(x,p,q,alpha). The shapes f1 and f2 are always taken
31to be end the end-points of the parameter alpha, regardless of what
32the those numeric values are.
33
34Since the value of fbar(x) cannot be easily calculated for a given value
35of x, class RooIntegralMorph is an implementation of RooAbsCachedPdf and
36calculates the shape of the interpolated p.d.f. fbar(x) for all values
37of x for a given value of alpha,p,q and caches these values in a histogram
38(as implemented by RooAbsCachedPdf). The binning granularity of the cache
39can be controlled by the binning named "cache" on the RooRealVar representing
40the observable x. The fbar sampling algorithm first scans the range of
41calculable c.d.f. values with a coarse grid and a recursive gap division
42mechanism, and then polishes the c.d.f. value for each cache bin center with
43Newton iterations on the monotone map X(y) = alpha*x1(y) + (1-alpha)*x2(y).
44The residual inaccuracy of the cached p.d.f. is therefore dominated by the
45interpolation between the cache bin centers and decreases steeply with the
46number of cache bins: with O(1000) cache bins the difference between the
47cached shape and the exact morphed p.d.f. is typically at the 1e-7 level
48(measured as a Kolmogorov-Smirnov distance) for Gaussian input shapes.
49
50Note on numeric stability of the algorithm. Since the algorithm relies
51on a numeric inversion of cumulative distributions functions, some precision
52may be lost at the 'edges' of the same (i.e. at regions in x where the
53c.d.f. value is close to zero or one). The sampling strategy is to start
54at y=0.1 (or the distance of y to 1.0) and push the y range outward by
55a factor of sqrt(10) iteratively up to the point where the corresponding
56x value no longer changes significantly, with a hard cutoff at y=1e-12.
57For p.d.f.s with very flat tails such as Gaussians some part of the tail
58may be lost due to limitations in numeric precision in the CDF inversion
59step.
60
61An effect related to the above limitation in numeric precision should
62be anticipated when floating the alpha parameter in a fit. If a p.d.f
63with such flat tails is fitted, it is likely that the dataset contains
64events in the flat tail region. If the alpha parameter is varied, the
65likelihood contribution from such events may exhibit discontinuities
66in alpha, causing discontinuities in the summed likelihood as well
67that will cause convergence problems in MINUIT. To mitigate this effect
68one can use the setCacheAlpha() method to instruct RooIntegralMorph
69to construct a two-dimensional cache for its output values in both
70x and alpha. If linear interpolation is requested on the resulting
71output histogram, the resulting interpolation of the p.d.f in the
72alpha dimension will smooth out the discontinuities in the tail regions
73result in a continuous likelihood distribution that can be fitted.
74An added advantage of the cacheAlpha option is that if parameters
75p,q of f1,f2 are fixed, the cached values in RooIntegralMorph are
76valid for the entire fit session and do not need to be recalculated
77for each change in alpha, which may result an considerable increase
78in calculation speed.
79
80**/
81
82#include "Riostream.h"
83
84#include "RooIntegralMorph.h"
85#include "RooAbsCategory.h"
86#include "RooBrentRootFinder.h"
87#include "RooAbsFunc.h"
88#include "RooRealVar.h"
89#include "RooDataHist.h"
90#include "TH1.h"
91
92using std::flush, std::endl;
93
94////////////////////////////////////////////////////////////////////////////////
95/// Constructor with observables x, pdf shapes pdf1 and pdf2 which represent
96/// the shapes at the end points of the interpolation parameter alpha
97/// If doCacheAlpha is true, a two-dimensional cache is constructed in
98/// both alpha and x
99
100RooIntegralMorph::RooIntegralMorph(const char *name, const char *title, RooAbsReal &_pdf1, RooAbsReal &_pdf2,
101 RooAbsReal &_x, RooAbsReal &_alpha, bool doCacheAlpha)
102 : RooAbsCachedPdf(name, title, 2),
103 pdf1("pdf1", "pdf1", this, _pdf1),
104 pdf2("pdf2", "pdf2", this, _pdf2),
105 x("x", "x", this, _x),
106 alpha("alpha", "alpha", this, _alpha),
107 _cacheAlpha(doCacheAlpha)
108{
109}
110
111////////////////////////////////////////////////////////////////////////////////
112/// Copy constructor
113
116 pdf1("pdf1", this, other.pdf1),
117 pdf2("pdf2", this, other.pdf2),
118 x("x", this, other.x),
119 alpha("alpha", this, other.alpha),
120 _cacheAlpha(other._cacheAlpha)
121{
122}
123
124////////////////////////////////////////////////////////////////////////////////
125/// Observable to be cached for given choice of normalization.
126/// Returns the 'x' observable unless doCacheAlpha is set in which
127/// case a set with both x and alpha
128
130{
131 RooArgSet *obs = new RooArgSet;
132 if (_cacheAlpha) {
133 obs->add(alpha.arg());
134 }
135 obs->add(x.arg());
137}
138
139////////////////////////////////////////////////////////////////////////////////
140/// Parameters of the cache. Returns parameters of both pdf1 and pdf2
141/// and parameter cache, in case doCacheAlpha is not set.
142
144{
145 std::unique_ptr<RooArgSet> par1{pdf1->getParameters(static_cast<RooArgSet *>(nullptr))};
147 pdf2->getParameters(nullptr, par2);
148 par1->add(par2, true);
149 par1->remove(x.arg(), true, true);
150 if (!_cacheAlpha) {
151 par1->add(alpha.arg());
152 }
153 return RooFit::makeOwningPtr(std::move(par1));
154}
155
156////////////////////////////////////////////////////////////////////////////////
157/// Return base name component for cache components in this case
158/// a string encoding the names of both end point p.d.f.s
159
161{
162 static TString name;
163
164 name = pdf1.arg().GetName();
165 name.Append("_MORPH_");
166 name.Append(pdf2.arg().GetName());
167 return name.Data();
168}
169
170////////////////////////////////////////////////////////////////////////////////
171/// Fill the cache with the interpolated shape.
172
174{
175 MorphCacheElem &mcache = static_cast<MorphCacheElem &>(cache);
176
177 // If cacheAlpha is true employ slice iterator here to fill all slices
178
179 if (!_cacheAlpha) {
180
181 std::unique_ptr<TIterator> dIter{cache.hist()->sliceIterator(const_cast<RooAbsReal &>(x.arg()), RooArgSet())};
182 mcache.calculate(dIter.get());
183
184 } else {
185 std::unique_ptr<TIterator> slIter{
186 cache.hist()->sliceIterator(const_cast<RooAbsReal &>(alpha.arg()), RooArgSet())};
187
188 double alphaSave = alpha;
190 coutP(Eval) << "RooIntegralMorph::fillCacheObject(" << GetName() << ") filling multi-dimensional cache";
191 while (slIter->Next()) {
192 alphaSet.assign(*cache.hist()->get());
193 std::unique_ptr<TIterator> dIter{
194 cache.hist()->sliceIterator(const_cast<RooAbsReal &>(x.arg()), RooArgSet(alpha.arg()))};
195 mcache.calculate(dIter.get());
196 ccoutP(Eval) << "." << flush;
197 }
198 ccoutP(Eval) << std::endl;
199
200 const_cast<RooIntegralMorph *>(this)->alpha = alphaSave;
201 }
202}
203
204////////////////////////////////////////////////////////////////////////////////
205/// Create and return a derived MorphCacheElem.
206
208{
209 return new MorphCacheElem(const_cast<RooIntegralMorph &>(*this), nset);
210}
211
212////////////////////////////////////////////////////////////////////////////////
213/// Return all RooAbsArg components contained in this cache
214
216{
218 ret.add(PdfCacheElem::containedArgs(action));
219 ret.add(*_self);
220 ret.add(*_pdf1);
221 ret.add(*_pdf2);
222 ret.add(*_x);
223 ret.add(*_alpha);
224 ret.add(*_c1);
225 ret.add(*_c2);
226
227 return ret;
228}
229
230////////////////////////////////////////////////////////////////////////////////
231/// Construct of cache element, copy relevant input from RooIntegralMorph,
232/// create the cdfs from the input p.d.fs and instantiate the root finders
233/// on the cdfs to perform the inversion.
234
237 _self(&self),
238 _pdf1(static_cast<RooAbsPdf *>(self.pdf1.absArg())),
239 _pdf2(static_cast<RooAbsPdf *>(self.pdf2.absArg())),
240 _x(static_cast<RooRealVar *>(self.x.absArg())),
241 _alpha(static_cast<RooAbsReal *>(self.alpha.absArg())),
242 _yatXmin(0),
243 _yatXmax(0),
244 _ccounter(0),
245 _ycutoff(1e-12)
246{
247 // Mark in base class that normalization of cached pdf is invariant under pdf parameters
248
249 _nset = std::make_unique<RooArgSet>(*_x);
250
251 _c1 = std::unique_ptr<RooAbsReal>{_pdf1->createCdf(*_x)};
252 _c2 = std::unique_ptr<RooAbsReal>{_pdf2->createCdf(*_x)};
253 _cb1 = std::unique_ptr<RooAbsFunc>{_c1->bindVars(*_x, _nset.get())};
254 _cb2 = std::unique_ptr<RooAbsFunc>{_c2->bindVars(*_x, _nset.get())};
255
256 _rf1 = std::make_unique<RooBrentRootFinder>(*_cb1);
257 _rf2 = std::make_unique<RooBrentRootFinder>(*_cb2);
258
259 _rf1->setTol(1e-12);
260 _rf2->setTol(1e-12);
261
262 // _yatX = 0 ;
263 // _calcX = 0 ;
264
265 // Must do this here too: fillCache() may not be called if cache contents is retrieved from EOcache
266 pdf()->setUnitNorm(true);
267}
268
269////////////////////////////////////////////////////////////////////////////////
270/// Destructor
271
273
274////////////////////////////////////////////////////////////////////////////////
275/// Calculate the x value of the output p.d.f at the given cdf value y.
276/// The ok boolean is filled with the success status of the operation.
277
279{
280 if (y < 0 || y > 1) {
281 oocoutW(_self, Eval)
282 << "RooIntegralMorph::MorphCacheElem::calcX() WARNING: requested root finding for unphysical CDF value " << y
283 << std::endl;
284 }
285 double x1;
286 double x2;
287
288 double xmax = _x->getMax("cache");
289 double xmin = _x->getMin("cache");
290
291 ok = true;
292 ok &= _rf1->findRoot(x1, xmin, xmax, y);
293 ok &= _rf2->findRoot(x2, xmin, xmax, y);
294 if (!ok)
295 return 0;
296 _ccounter++;
297
298 return _alpha->getVal() * x1 + (1 - _alpha->getVal()) * x2;
299}
300
301////////////////////////////////////////////////////////////////////////////////
302/// Return the bin number enclosing the given x value
303
305{
306 double xmax = _x->getMax("cache");
307 double xmin = _x->getMin("cache");
308 return (Int_t)(_x->numBins("cache") * (X - xmin) / (xmax - xmin));
309}
310
311////////////////////////////////////////////////////////////////////////////////
312/// Calculate shape of p.d.f for x,alpha values
313/// defined by dIter iterator over cache histogram
314
316{
317 double xsave = _self->x;
318
319 // if (!_yatX) {
320 // _yatX = new double[_x->numBins("cache")+1] ;
321 // _calcX = new double[_x->numBins("cache")+1] ;
322 // }
323
324 _yatX.resize(_x->numBins("cache") + 1);
325 _calcX.resize(_x->numBins("cache") + 1);
326
327 _ccounter = 0;
328
329 // Get number of bins from PdfCacheElem histogram
330 Int_t nbins = _x->numBins("cache");
331 if (nbins < 2) {
332 oocoutE(_self, Eval) << "RooIntegralMorph::MorphCacheElem::calculate(" << _self->GetName()
333 << ") ERROR: observable " << _x->GetName()
334 << " has an empty binning for the cache histogram."
335 << " Define one with RooRealVar::setBins(nbins, \"cache\")." << std::endl;
336 return;
337 }
338
339 // Initialize yatX array to 'un-calculated values (-1)'
340 for (int i = 0; i < nbins; i++) {
341 _yatX[i] = -1;
342 _calcX[i] = 0;
343 }
344
345 // Find low and high point
346 findRange();
347
348 // Perform initial scan of 100 points
349 for (int i = 0; i < 10; i++) {
350
351 // Take a point in y
352 double offset = _yatX[_yatXmin];
353 double delta = (_yatX[_yatXmax] - _yatX[_yatXmin]) / 10;
354 double y = offset + i * delta;
355
356 // Calculate corresponding X
357 bool ok;
358 double X = calcX(y, ok);
359 if (ok) {
360 Int_t iX = binX(X);
361 _yatX[iX] = y;
362 _calcX[iX] = X;
363 }
364 }
365
366 // Now take an iteration filling the 'gaps'
367 Int_t igapLow = _yatXmin + 1;
368 while (true) {
369 // Find next gap
370 Int_t igapHigh = igapLow + 1;
371 while (igapHigh < (_yatXmax) && _yatX[igapHigh] < 0)
372 igapHigh++;
373
374 // Fill the gap (iteratively and/or using interpolation)
375 fillGap(igapLow - 1, igapHigh);
376
377 // Terminate after processing of last gap
378 if (igapHigh >= _yatXmax - 1)
379 break;
380 igapLow = igapHigh + 1;
381 }
382
383 // Make one more iteration to recalculate Y value at bin centers
384 double xmax = _x->getMax("cache");
385 double xmin = _x->getMin("cache");
386 double binw = (xmax - xmin) / _x->numBins("cache");
387 for (int i = _yatXmin + 1; i < _yatXmax - 1; i++) {
388
389 // Calculate additional offset to apply if bin ixlo does not have X value calculated at bin center
390 double xBinC = xmin + (i + 0.5) * binw;
391 double xOffset = xBinC - _calcX[i];
392 if (std::abs(xOffset / binw) > 1e-3) {
393 double slope = (_yatX[i + 1] - _yatX[i - 1]) / (_calcX[i + 1] - _calcX[i - 1]);
394 double newY = _yatX[i] + slope * xOffset;
395 // cout << "bin " << i << " needs to be re-centered " << xOffset/binw << " slope = " << slope << " origY = " <<
396 // _yatX[i] << " newY = " << newY << std::endl ;
397 _yatX[i] = newY;
398 }
399 }
400
401 // Zero output histogram below lowest calculable X value
402 for (int i = 0; i < _yatXmin; i++) {
403 dIter->Next();
404 const std::size_t binIdx = hist()->getIndex(*hist()->get(), /*fast=*/true);
405 hist()->set(binIdx, 0, -1);
406 }
407
408 double xMax = _x->getMax("cache");
409 const double aval = _alpha->getVal();
410
411 // Lower bounds for the root finding in the loop below, exploiting the fact
412 // that the cumulative distribution functions increase monotonically: as y
413 // increases from bin to bin, the x values found in the previous bin are
414 // valid lower bounds for the current bin.
415 double x1lo = _x->getMin("cache");
416 double x2lo = _x->getMin("cache");
417
418 // Transfer calculated values to histogram
419 for (int i = _yatXmin; i <= _yatXmax; i++) {
420
421 double y = _yatX[i];
422 const double xBinC = xmin + (i + 0.5) * binw;
423
424 double x1 = x1lo;
425 double x2 = x2lo;
426 double f1x1 = 0;
427 double f2x2 = 0;
428
429 // The y values obtained from the recursive gap filling are only
430 // approximate solutions of X(y) == xBinC. Polish them with Newton
431 // iterations on the monotone map X(y) = alpha*x1(y) + (1-alpha)*x2(y),
432 // whose derivative is dX/dy = alpha/f1(x1) + (1-alpha)/f2(x2), so that
433 // the stored p.d.f. value corresponds to the bin center to full precision.
434 for (int iter = 0; iter < 10; iter++) {
435 bool ok = _rf1->findRoot(x1, x1lo, xMax, y);
436 ok &= _rf2->findRoot(x2, x2lo, xMax, y);
437 _x->setVal(x1);
438 f1x1 = _pdf1->getVal(_nset.get());
439 _x->setVal(x2);
440 f2x2 = _pdf2->getVal(_nset.get());
441 if (!ok || f1x1 <= 0 || f2x2 <= 0)
442 break;
443 const double X = aval * x1 + (1 - aval) * x2;
444 if (std::abs(X - xBinC) < 1e-12 * (xMax - xmin))
445 break;
446 const double dXdy = aval / f1x1 + (1 - aval) / f2x2;
447 const double yNew = y + (xBinC - X) / dXdy;
448 if (!(yNew > 0.) || !(yNew < 1.) || yNew == y)
449 break;
450 y = yNew;
451 }
452 _yatX[i] = y;
453
454 double fbarX = f1x1 * f2x2 / (aval * f2x2 + (1 - aval) * f1x1);
455
456 dIter->Next();
457 {
458 const std::size_t binIdx = hist()->getIndex(*hist()->get(), /*fast=*/true);
459 hist()->set(binIdx, fbarX, -1);
460 }
461
462 x1lo = x1;
463 x2lo = x2;
464 }
465 // Zero output histogram above highest calculable X value
466 for (int i = _yatXmax + 1; i < nbins; i++) {
467 dIter->Next();
468 const std::size_t binIdx = hist()->getIndex(*hist()->get(), /*fast=*/true);
469 hist()->set(binIdx, 0, -1);
470 }
471
472 pdf()->setUnitNorm(true);
473 _self->x = xsave;
474
475 oocxcoutD(_self, Eval) << "RooIntegralMorph::MorphCacheElem::calculate(" << _self->GetName()
476 << ") calculation required " << _ccounter << " samplings of cdfs" << std::endl;
477}
478
479////////////////////////////////////////////////////////////////////////////////
480/// Fill all empty histogram bins between bins ixlo and ixhi. The value of 'splitPoint'
481/// defines the split point for the recursive division strategy to fill the gaps
482/// If the midpoint value of y is very close to the midpoint in x, use interpolation
483/// to fill the gaps, otherwise the intervals again.
484
486{
487 // CONVENTION: _yatX[ixlo] is filled, _yatX[ixhi] is filled, elements in between are empty
488 // std::cout << "fillGap: gap from _yatX[" << ixlo << "]=" << _yatX[ixlo] << " to _yatX[" << ixhi << "]=" <<
489 // _yatX[ixhi] << ", size = " << ixhi-ixlo << std::endl ;
490
491 if (_yatX[ixlo] < 0) {
492 oocoutE(_self, Eval) << "RooIntegralMorph::MorphCacheElme::fillGap(" << _self->GetName() << "): ERROR in fillgap "
493 << ixlo << " = " << ixhi << " splitPoint= " << splitPoint << " _yatX[ixlo] = " << _yatX[ixlo]
494 << std::endl;
495 }
496 if (_yatX[ixhi] < 0) {
497 oocoutE(_self, Eval) << "RooIntegralMorph::MorphCacheElme::fillGap(" << _self->GetName() << "): ERROR in fillgap "
498 << ixlo << " = " << ixhi << " splitPoint " << splitPoint << " _yatX[ixhi] = " << _yatX[ixhi]
499 << std::endl;
500 }
501
502 // Determine where half-way Y value lands
503 double ymid = _yatX[ixlo] * splitPoint + _yatX[ixhi] * (1 - splitPoint);
504 bool ok;
505 double Xmid = calcX(ymid, ok);
506 if (!ok) {
507 oocoutW(_self, Eval) << "RooIntegralMorph::MorphCacheElem::fillGap(" << _self->GetName()
508 << ") unable to calculate midpoint in gap [" << ixlo << "," << ixhi
509 << "], resorting to interpolation" << std::endl;
510 interpolateGap(ixlo, ixhi);
511 }
512
513 Int_t iX = binX(Xmid);
514 double cq = (Xmid - _calcX[ixlo]) / (_calcX[ixhi] - _calcX[ixlo]) - 0.5;
515
516 // Store midway point
517 _yatX[iX] = ymid;
518 _calcX[iX] = Xmid;
519
520 // Policy: If centration quality is better than 1% OR better than 1/10 of a bin, fill interval with linear
521 // interpolation
522 if (std::abs(cq) < 0.01 || std::abs(cq * (ixhi - ixlo)) < 0.1 || ymid < _ycutoff) {
523
524 // Fill remaining gaps on either side with linear interpolation
525 if (iX - ixlo > 1) {
526 interpolateGap(ixlo, iX);
527 }
528 if (ixhi - iX > 1) {
529 interpolateGap(iX, ixhi);
530 }
531
532 } else {
533
534 if (iX == ixlo) {
535
536 if (splitPoint < 0.95) {
537 // Midway value lands on lowest bin, retry split with higher split point
538 double newSplit = splitPoint + 0.5 * (1 - splitPoint);
539 fillGap(ixlo, ixhi, newSplit);
540 } else {
541 // Give up and resort to interpolation
542 interpolateGap(ixlo, ixhi);
543 }
544
545 } else if (iX == ixhi) {
546
547 // Midway value lands on highest bin, retry split with lower split point
548 if (splitPoint > 0.05) {
549 double newSplit = splitPoint / 2;
550 fillGap(ixlo, ixhi, newSplit);
551 } else {
552 // Give up and resort to interpolation
553 interpolateGap(ixlo, ixhi);
554 }
555
556 } else {
557
558 // Midway point reasonable, iterate on interval on both sides
559 if (iX - ixlo > 1) {
560 fillGap(ixlo, iX);
561 }
562 if (ixhi - iX > 1) {
563 fillGap(iX, ixhi);
564 }
565 }
566 }
567}
568
569////////////////////////////////////////////////////////////////////////////////
570/// Fill empty histogram bins between ixlo and ixhi with values obtained
571/// from linear interpolation of ixlo,ixhi elements.
572
574{
575 // cout << "filling gap with linear interpolation ixlo=" << ixlo << " ixhi=" << ixhi << std::endl ;
576
577 double xmax = _x->getMax("cache");
578 double xmin = _x->getMin("cache");
579 double binw = (xmax - xmin) / _x->numBins("cache");
580
581 // Calculate deltaY in terms of actual X difference calculate, not based on nominal bin width
582 double deltaY = (_yatX[ixhi] - _yatX[ixlo]) / ((_calcX[ixhi] - _calcX[ixlo]) / binw);
583
584 // Calculate additional offset to apply if bin ixlo does not have X value calculated at bin center
585 double xBinC = xmin + (ixlo + 0.5) * binw;
586 double xOffset = xBinC - _calcX[ixlo];
587
588 for (int j = ixlo + 1; j < ixhi; j++) {
589 _yatX[j] = _yatX[ixlo] + (xOffset / binw + (j - ixlo)) * deltaY;
590 _calcX[j] = xmin + (j + 0.5) * binw;
591 }
592}
593
594////////////////////////////////////////////////////////////////////////////////
595/// Determine which range of y values can be mapped to x values
596/// from the numeric inversion of the input c.d.fs.
597/// Start with a y range of [0.1-0.9] and push boundaries
598/// outward with a factor of 1/sqrt(10). Stop iteration if
599/// inverted x values no longer change
600
602{
603 double xmin = _x->getMin("cache");
604 double xmax = _x->getMax("cache");
605 Int_t nbins = _x->numBins("cache");
606
607 double x1;
608 double x2;
609 bool ok = true;
610 double ymin = 0.1;
611 double yminSave(-1);
612 double Xsave(-1);
613 double Xlast = xmax;
614
615 // Find lowest Y value that can be measured
616 // Start at 0.1 and iteratively lower limit by sqrt(10)
617 while (true) {
618 ok &= _rf1->findRoot(x1, xmin, xmax, ymin);
619 ok &= _rf2->findRoot(x2, xmin, xmax, ymin);
620 oocxcoutD(_self, Eval) << "RooIntegralMorph::MorphCacheElem::findRange(" << _self->GetName()
621 << ") findMin: x1 = " << x1 << " x2 = " << x2 << " ok = " << (ok ? "T" : "F") << std::endl;
622
623 // Terminate in case of non-convergence
624 if (!ok)
625 break;
626
627 // Terminate if value of X no longer moves by >0.1 bin size
628 double X = _alpha->getVal() * x1 + (1 - _alpha->getVal()) * x2;
629 if (std::abs(X - Xlast) / (xmax - xmin) < 0.0001) {
630 break;
631 }
632 Xlast = X;
633
634 // Store new Y value
635 _yatXmin = (Int_t)(nbins * (X - xmin) / (xmax - xmin));
636 _yatX[_yatXmin] = ymin;
637 _calcX[_yatXmin] = X;
638 yminSave = ymin;
639 Xsave = X;
640
641 // Reduce ymin by half an order of magnitude
642 ymin /= sqrt(10.);
643
644 // Emergency break
645 if (ymin < _ycutoff)
646 break;
647 }
648 _yatX[_yatXmin] = yminSave;
649 _calcX[_yatXmin] = Xsave;
650
651 // Find highest Y value that can be measured
652 // Start at 1 - 0.1 and iteratively lower delta by sqrt(10)
653 ok = true;
654 double deltaymax = 0.1;
655 double deltaymaxSave(-1);
656 Xlast = xmin;
657 while (true) {
658 ok &= _rf1->findRoot(x1, xmin, xmax, 1 - deltaymax);
659 ok &= _rf2->findRoot(x2, xmin, xmax, 1 - deltaymax);
660
661 oocxcoutD(_self, Eval) << "RooIntegralMorph::MorphCacheElem::findRange(" << _self->GetName()
662 << ") findMax: x1 = " << x1 << " x2 = " << x2 << " ok = " << (ok ? "T" : "F") << std::endl;
663
664 // Terminate in case of non-convergence
665 if (!ok)
666 break;
667
668 // Terminate if value of X no longer moves by >0.1 bin size
669 double X = _alpha->getVal() * x1 + (1 - _alpha->getVal()) * x2;
670 if (std::abs(X - Xlast) / (xmax - xmin) < 0.0001) {
671 break;
672 }
673 Xlast = X;
674
675 // Store new Y value
676 _yatXmax = (Int_t)(nbins * (X - xmin) / (xmax - xmin));
677 _yatX[_yatXmax] = 1 - deltaymax;
678 _calcX[_yatXmax] = X;
680 Xsave = X;
681
682 // Reduce ymin by half an order of magnitude
683 deltaymax /= sqrt(10.);
684
685 // Emergency break
686 if (deltaymax < _ycutoff)
687 break;
688 }
689
690 _yatX[_yatXmax] = 1 - deltaymaxSave;
691 _calcX[_yatXmax] = Xsave;
692
693 // Initialize values out of range to 'out-of-range' (-2)
694 for (int i = 0; i < _yatXmin; i++)
695 _yatX[i] = -2;
696 for (int i = _yatXmax + 1; i < nbins; i++)
697 _yatX[i] = -2;
698 oocxcoutD(_self, Eval) << "RooIntegralMorph::findRange(" << _self->GetName() << "): ymin = " << _yatX[_yatXmin]
699 << " ymax = " << _yatX[_yatXmax] << std::endl;
700 oocxcoutD(_self, Eval) << "RooIntegralMorph::findRange(" << _self->GetName() << "): xmin = " << _calcX[_yatXmin]
701 << " xmax = " << _calcX[_yatXmax] << std::endl;
702}
703
704////////////////////////////////////////////////////////////////////////////////
705/// Dummy
706
708{
709 return 0;
710}
711
712////////////////////////////////////////////////////////////////////////////////
713/// Indicate to the RooAbsCachedPdf base class that for the filling of the
714/// cache the traversal of the x should be in the innermost loop, to minimize
715/// recalculation of the one-dimensional internal cache for a fixed value of alpha
716
718{
719 // Put x last to minimize cache faulting
720 orderedObs.removeAll();
721
722 orderedObs.add(obs);
723 RooAbsArg *obsX = obs.find(x.arg().GetName());
724 if (obsX) {
725 orderedObs.remove(*obsX);
726 orderedObs.add(*obsX);
727 }
728}
#define e(i)
Definition RSha256.hxx:103
#define coutP(a)
#define oocoutW(o, a)
#define oocxcoutD(o, a)
#define ccoutP(a)
#define oocoutE(o, a)
int Int_t
Signed integer 4 bytes (int)
Definition RtypesCore.h:59
#define X(type, name)
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
Option_t Option_t TPoint TPoint const char x2
Option_t Option_t TPoint TPoint const char x1
char name[80]
Definition TGX11.cxx:145
float xmin
float ymin
float xmax
Common abstract base class for objects that represent a value and a "shape" in RooFit.
Definition RooAbsArg.h:76
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...
Abstract base class for p.d.f.s that need or want to cache their evaluate() output in a RooHistPdf de...
virtual bool add(const RooAbsArg &var, bool silent=false)
Add the specified argument to list.
RooAbsArg * find(const char *name) const
Find object with given name in list.
Abstract interface for all probability density functions.
Definition RooAbsPdf.h:32
RooFit::OwningPtr< RooAbsReal > createCdf(const RooArgSet &iset, const RooArgSet &nset=RooArgSet())
Create a cumulative distribution function of this p.d.f in terms of the observables listed in iset.
Abstract base class for objects that represent a real value and implements functionality common to al...
Definition RooAbsReal.h:63
virtual RooFit::OwningPtr< RooFitResult > To fit data with errors in and x and y
Definition RooAbsReal.h:200
virtual double offset() const
Definition RooAbsReal.h:395
RooArgList is a container object that can hold multiple RooAbsArg objects.
Definition RooArgList.h:22
RooArgSet is a container object that can hold multiple RooAbsArg objects.
Definition RooArgSet.h:24
TIterator * sliceIterator(RooAbsArg &sliceArg, const RooArgSet &otherArgs)
Create an iterator over all bins in a slice defined by the subset of observables listed in sliceArg.
const RooArgSet * get() const override
Get bin centre of current bin.
Definition RooDataHist.h:82
void setUnitNorm(bool flag)
Definition RooHistPdf.h:78
std::unique_ptr< RooBrentRootFinder > _rf1
std::unique_ptr< RooAbsReal > _c1
void calculate(TIterator *iter)
Calculate shape of p.d.f for x,alpha values defined by dIter iterator over cache histogram.
void interpolateGap(Int_t ixlo, Int_t ixhi)
Fill empty histogram bins between ixlo and ixhi with values obtained from linear interpolation of ixl...
MorphCacheElem(RooIntegralMorph &self, const RooArgSet *nset)
Construct of cache element, copy relevant input from RooIntegralMorph, create the cdfs from the input...
std::unique_ptr< RooAbsFunc > _cb2
void fillGap(Int_t ixlo, Int_t ixhi, double splitPoint=0.5)
Fill all empty histogram bins between bins ixlo and ixhi.
std::unique_ptr< RooArgSet > _nset
void findRange()
Determine which range of y values can be mapped to x values from the numeric inversion of the input c...
std::unique_ptr< RooAbsFunc > _cb1
std::unique_ptr< RooBrentRootFinder > _rf2
RooArgList containedArgs(Action) override
Return all RooAbsArg components contained in this cache.
std::unique_ptr< RooAbsReal > _c2
double calcX(double y, bool &ok)
Calculate the x value of the output p.d.f at the given cdf value y.
Int_t binX(double x)
Return the bin number enclosing the given x value.
Class RooIntegralMorph is an implementation of the histogram interpolation technique described by Ale...
RooIntegralMorph()=default
RooFit::OwningPtr< RooArgSet > actualObservables(const RooArgSet &nset) const override
Observable to be cached for given choice of normalization.
friend class MorphCacheElem
PdfCacheElem * createCache(const RooArgSet *nset) const override
Create and return a derived MorphCacheElem.
const char * inputBaseName() const override
Return base name component for cache components in this case a string encoding the names of both end ...
void preferredObservableScanOrder(const RooArgSet &obs, RooArgSet &orderedObs) const override
Indicate to the RooAbsCachedPdf base class that for the filling of the cache the traversal of the x s...
void fillCacheObject(PdfCacheElem &cache) const override
Fill the cache with the interpolated shape.
double evaluate() const override
Dummy.
RooFit::OwningPtr< RooArgSet > actualParameters(const RooArgSet &nset) const override
Parameters of the cache.
Variable that can be changed from the outside.
Definition RooRealVar.h:37
const T & arg() const
Return reference to object held in proxy.
Iterator abstract base class.
Definition TIterator.h:30
const char * GetName() const override
Returns name of object.
Definition TNamed.h:49
Basic string class.
Definition TString.h:138
Double_t x[n]
Definition legend1.C:17
T * OwningPtr
An alias for raw pointers for indicating that the return type of a RooFit function is an owning point...
Definition Config.h:35
OwningPtr< T > makeOwningPtr(std::unique_ptr< T > &&ptr)
Internal helper to turn a std::unique_ptr<T> into an OwningPtr.
Definition Config.h:40