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 "RooIntegralMorph.h"
83#include "RooAbsCategory.h"
84#include "RooBrentRootFinder.h"
85#include "RooAbsFunc.h"
86#include "RooRealVar.h"
87#include "RooDataHist.h"
88
89using std::flush, std::endl;
90
91////////////////////////////////////////////////////////////////////////////////
92/// Constructor with observables x, pdf shapes pdf1 and pdf2 which represent
93/// the shapes at the end points of the interpolation parameter alpha
94/// If doCacheAlpha is true, a two-dimensional cache is constructed in
95/// both alpha and x
96
97RooIntegralMorph::RooIntegralMorph(const char *name, const char *title, RooAbsReal &_pdf1, RooAbsReal &_pdf2,
98 RooAbsReal &_x, RooAbsReal &_alpha, bool doCacheAlpha)
99 : RooAbsCachedPdf(name, title, 2),
100 pdf1("pdf1", "pdf1", this, _pdf1),
101 pdf2("pdf2", "pdf2", this, _pdf2),
102 x("x", "x", this, _x),
103 alpha("alpha", "alpha", this, _alpha),
104 _cacheAlpha(doCacheAlpha)
105{
106}
107
108////////////////////////////////////////////////////////////////////////////////
109/// Copy constructor
110
113 pdf1("pdf1", this, other.pdf1),
114 pdf2("pdf2", this, other.pdf2),
115 x("x", this, other.x),
116 alpha("alpha", this, other.alpha),
117 _cacheAlpha(other._cacheAlpha)
118{
119}
120
121////////////////////////////////////////////////////////////////////////////////
122/// Observable to be cached for given choice of normalization.
123/// Returns the 'x' observable unless doCacheAlpha is set in which
124/// case a set with both x and alpha
125
127{
128 RooArgSet *obs = new RooArgSet;
129 if (_cacheAlpha) {
130 obs->add(alpha.arg());
131 }
132 obs->add(x.arg());
134}
135
136////////////////////////////////////////////////////////////////////////////////
137/// Parameters of the cache. Returns parameters of both pdf1 and pdf2
138/// and parameter cache, in case doCacheAlpha is not set.
139
141{
142 std::unique_ptr<RooArgSet> par1{pdf1->getParameters(static_cast<RooArgSet *>(nullptr))};
144 pdf2->getParameters(nullptr, par2);
145 par1->add(par2, true);
146 par1->remove(x.arg(), true, true);
147 if (!_cacheAlpha) {
148 par1->add(alpha.arg());
149 }
150 return RooFit::makeOwningPtr(std::move(par1));
151}
152
153////////////////////////////////////////////////////////////////////////////////
154/// Return base name component for cache components in this case
155/// a string encoding the names of both end point p.d.f.s
156
158{
159 static TString name;
160
161 name = pdf1.arg().GetName();
162 name.Append("_MORPH_");
163 name.Append(pdf2.arg().GetName());
164 return name.Data();
165}
166
167////////////////////////////////////////////////////////////////////////////////
168/// Fill the cache with the interpolated shape.
169
171{
172 MorphCacheElem &mcache = static_cast<MorphCacheElem &>(cache);
173
174 // If cacheAlpha is true employ slice iterator here to fill all slices
175
176 if (!_cacheAlpha) {
177
178 std::unique_ptr<TIterator> dIter{cache.hist()->sliceIterator(const_cast<RooAbsReal &>(x.arg()), RooArgSet())};
179 mcache.calculate(dIter.get());
180
181 } else {
182 std::unique_ptr<TIterator> slIter{
183 cache.hist()->sliceIterator(const_cast<RooAbsReal &>(alpha.arg()), RooArgSet())};
184
185 double alphaSave = alpha;
187 coutP(Eval) << "RooIntegralMorph::fillCacheObject(" << GetName() << ") filling multi-dimensional cache";
188 while (slIter->Next()) {
189 alphaSet.assign(*cache.hist()->get());
190 std::unique_ptr<TIterator> dIter{
191 cache.hist()->sliceIterator(const_cast<RooAbsReal &>(x.arg()), RooArgSet(alpha.arg()))};
192 mcache.calculate(dIter.get());
193 ccoutP(Eval) << "." << flush;
194 }
195 ccoutP(Eval) << std::endl;
196
197 const_cast<RooIntegralMorph *>(this)->alpha = alphaSave;
198 }
199}
200
201////////////////////////////////////////////////////////////////////////////////
202/// Create and return a derived MorphCacheElem.
203
205{
206 return new MorphCacheElem(const_cast<RooIntegralMorph &>(*this), nset);
207}
208
209////////////////////////////////////////////////////////////////////////////////
210/// Return all RooAbsArg components contained in this cache
211
213{
215 ret.add(PdfCacheElem::containedArgs(action));
216 ret.add(*_self);
217 ret.add(*_pdf1);
218 ret.add(*_pdf2);
219 ret.add(*_x);
220 ret.add(*_alpha);
221 ret.add(*_c1);
222 ret.add(*_c2);
223
224 return ret;
225}
226
227////////////////////////////////////////////////////////////////////////////////
228/// Construct of cache element, copy relevant input from RooIntegralMorph,
229/// create the cdfs from the input p.d.fs and instantiate the root finders
230/// on the cdfs to perform the inversion.
231
234 _self(&self),
235 _pdf1(static_cast<RooAbsPdf *>(self.pdf1.absArg())),
236 _pdf2(static_cast<RooAbsPdf *>(self.pdf2.absArg())),
237 _x(static_cast<RooRealVar *>(self.x.absArg())),
238 _alpha(static_cast<RooAbsReal *>(self.alpha.absArg())),
239 _yatXmin(0),
240 _yatXmax(0),
241 _ccounter(0),
242 _ycutoff(1e-12)
243{
244 // Mark in base class that normalization of cached pdf is invariant under pdf parameters
245
246 _nset = std::make_unique<RooArgSet>(*_x);
247
248 _c1 = std::unique_ptr<RooAbsReal>{_pdf1->createCdf(*_x)};
249 _c2 = std::unique_ptr<RooAbsReal>{_pdf2->createCdf(*_x)};
250 _cb1 = std::unique_ptr<RooAbsFunc>{_c1->bindVars(*_x, _nset.get())};
251 _cb2 = std::unique_ptr<RooAbsFunc>{_c2->bindVars(*_x, _nset.get())};
252
253 _rf1 = std::make_unique<RooBrentRootFinder>(*_cb1);
254 _rf2 = std::make_unique<RooBrentRootFinder>(*_cb2);
255
256 _rf1->setTol(1e-12);
257 _rf2->setTol(1e-12);
258
259 // _yatX = 0 ;
260 // _calcX = 0 ;
261
262 // Must do this here too: fillCache() may not be called if cache contents is retrieved from EOcache
263 pdf()->setUnitNorm(true);
264}
265
266////////////////////////////////////////////////////////////////////////////////
267/// Destructor
268
270
271////////////////////////////////////////////////////////////////////////////////
272/// Calculate the x value of the output p.d.f at the given cdf value y.
273/// The ok boolean is filled with the success status of the operation.
274
276{
277 if (y < 0 || y > 1) {
278 oocoutW(_self, Eval)
279 << "RooIntegralMorph::MorphCacheElem::calcX() WARNING: requested root finding for unphysical CDF value " << y
280 << std::endl;
281 }
282 double x1;
283 double x2;
284
285 double xmax = _x->getMax("cache");
286 double xmin = _x->getMin("cache");
287
288 ok = true;
289 ok &= _rf1->findRoot(x1, xmin, xmax, y);
290 ok &= _rf2->findRoot(x2, xmin, xmax, y);
291 if (!ok)
292 return 0;
293 _ccounter++;
294
295 return _alpha->getVal() * x1 + (1 - _alpha->getVal()) * x2;
296}
297
298////////////////////////////////////////////////////////////////////////////////
299/// Return the bin number enclosing the given x value
300
302{
303 double xmax = _x->getMax("cache");
304 double xmin = _x->getMin("cache");
305 return (Int_t)(_x->numBins("cache") * (X - xmin) / (xmax - xmin));
306}
307
308////////////////////////////////////////////////////////////////////////////////
309/// Calculate shape of p.d.f for x,alpha values
310/// defined by dIter iterator over cache histogram
311
313{
314 double xsave = _self->x;
315
316 // if (!_yatX) {
317 // _yatX = new double[_x->numBins("cache")+1] ;
318 // _calcX = new double[_x->numBins("cache")+1] ;
319 // }
320
321 _yatX.resize(_x->numBins("cache") + 1);
322 _calcX.resize(_x->numBins("cache") + 1);
323
324 _ccounter = 0;
325
326 // Get number of bins from PdfCacheElem histogram
327 Int_t nbins = _x->numBins("cache");
328 if (nbins < 2) {
329 oocoutE(_self, Eval) << "RooIntegralMorph::MorphCacheElem::calculate(" << _self->GetName()
330 << ") ERROR: observable " << _x->GetName()
331 << " has an empty binning for the cache histogram."
332 << " Define one with RooRealVar::setBins(nbins, \"cache\")." << std::endl;
333 return;
334 }
335
336 // Initialize yatX array to 'un-calculated values (-1)'
337 for (int i = 0; i < nbins; i++) {
338 _yatX[i] = -1;
339 _calcX[i] = 0;
340 }
341
342 // Find low and high point
343 findRange();
344
345 // Perform initial scan of 100 points
346 for (int i = 0; i < 10; i++) {
347
348 // Take a point in y
349 double offset = _yatX[_yatXmin];
350 double delta = (_yatX[_yatXmax] - _yatX[_yatXmin]) / 10;
351 double y = offset + i * delta;
352
353 // Calculate corresponding X
354 bool ok;
355 double X = calcX(y, ok);
356 if (ok) {
357 Int_t iX = binX(X);
358 _yatX[iX] = y;
359 _calcX[iX] = X;
360 }
361 }
362
363 // Now take an iteration filling the 'gaps'
364 Int_t igapLow = _yatXmin + 1;
365 while (true) {
366 // Find next gap
367 Int_t igapHigh = igapLow + 1;
368 while (igapHigh < (_yatXmax) && _yatX[igapHigh] < 0)
369 igapHigh++;
370
371 // Fill the gap (iteratively and/or using interpolation)
372 fillGap(igapLow - 1, igapHigh);
373
374 // Terminate after processing of last gap
375 if (igapHigh >= _yatXmax - 1)
376 break;
377 igapLow = igapHigh + 1;
378 }
379
380 // Make one more iteration to recalculate Y value at bin centers
381 double xmax = _x->getMax("cache");
382 double xmin = _x->getMin("cache");
383 double binw = (xmax - xmin) / _x->numBins("cache");
384 for (int i = _yatXmin + 1; i < _yatXmax - 1; i++) {
385
386 // Calculate additional offset to apply if bin ixlo does not have X value calculated at bin center
387 double xBinC = xmin + (i + 0.5) * binw;
388 double xOffset = xBinC - _calcX[i];
389 if (std::abs(xOffset / binw) > 1e-3) {
390 double slope = (_yatX[i + 1] - _yatX[i - 1]) / (_calcX[i + 1] - _calcX[i - 1]);
391 double newY = _yatX[i] + slope * xOffset;
392 // cout << "bin " << i << " needs to be re-centered " << xOffset/binw << " slope = " << slope << " origY = " <<
393 // _yatX[i] << " newY = " << newY << std::endl ;
394 _yatX[i] = newY;
395 }
396 }
397
398 // Zero output histogram below lowest calculable X value
399 for (int i = 0; i < _yatXmin; i++) {
400 dIter->Next();
401 const std::size_t binIdx = hist()->getIndex(*hist()->get(), /*fast=*/true);
402 hist()->set(binIdx, 0, -1);
403 }
404
405 double xMax = _x->getMax("cache");
406 const double aval = _alpha->getVal();
407
408 // Lower bounds for the root finding in the loop below, exploiting the fact
409 // that the cumulative distribution functions increase monotonically: as y
410 // increases from bin to bin, the x values found in the previous bin are
411 // valid lower bounds for the current bin.
412 double x1lo = _x->getMin("cache");
413 double x2lo = _x->getMin("cache");
414
415 // Transfer calculated values to histogram
416 for (int i = _yatXmin; i <= _yatXmax; i++) {
417
418 double y = _yatX[i];
419 const double xBinC = xmin + (i + 0.5) * binw;
420
421 double x1 = x1lo;
422 double x2 = x2lo;
423 double f1x1 = 0;
424 double f2x2 = 0;
425
426 // The y values obtained from the recursive gap filling are only
427 // approximate solutions of X(y) == xBinC. Polish them with Newton
428 // iterations on the monotone map X(y) = alpha*x1(y) + (1-alpha)*x2(y),
429 // whose derivative is dX/dy = alpha/f1(x1) + (1-alpha)/f2(x2), so that
430 // the stored p.d.f. value corresponds to the bin center to full precision.
431 for (int iter = 0; iter < 10; iter++) {
432 bool ok = _rf1->findRoot(x1, x1lo, xMax, y);
433 ok &= _rf2->findRoot(x2, x2lo, xMax, y);
434 _x->setVal(x1);
435 f1x1 = _pdf1->getVal(_nset.get());
436 _x->setVal(x2);
437 f2x2 = _pdf2->getVal(_nset.get());
438 if (!ok || f1x1 <= 0 || f2x2 <= 0)
439 break;
440 const double X = aval * x1 + (1 - aval) * x2;
441 if (std::abs(X - xBinC) < 1e-12 * (xMax - xmin))
442 break;
443 const double dXdy = aval / f1x1 + (1 - aval) / f2x2;
444 const double yNew = y + (xBinC - X) / dXdy;
445 if (!(yNew > 0.) || !(yNew < 1.) || yNew == y)
446 break;
447 y = yNew;
448 }
449 _yatX[i] = y;
450
451 double fbarX = f1x1 * f2x2 / (aval * f2x2 + (1 - aval) * f1x1);
452
453 dIter->Next();
454 {
455 const std::size_t binIdx = hist()->getIndex(*hist()->get(), /*fast=*/true);
456 hist()->set(binIdx, fbarX, -1);
457 }
458
459 x1lo = x1;
460 x2lo = x2;
461 }
462 // Zero output histogram above highest calculable X value
463 for (int i = _yatXmax + 1; i < nbins; i++) {
464 dIter->Next();
465 const std::size_t binIdx = hist()->getIndex(*hist()->get(), /*fast=*/true);
466 hist()->set(binIdx, 0, -1);
467 }
468
469 pdf()->setUnitNorm(true);
470 _self->x = xsave;
471
472 oocxcoutD(_self, Eval) << "RooIntegralMorph::MorphCacheElem::calculate(" << _self->GetName()
473 << ") calculation required " << _ccounter << " samplings of cdfs" << std::endl;
474}
475
476////////////////////////////////////////////////////////////////////////////////
477/// Fill all empty histogram bins between bins ixlo and ixhi. The value of 'splitPoint'
478/// defines the split point for the recursive division strategy to fill the gaps
479/// If the midpoint value of y is very close to the midpoint in x, use interpolation
480/// to fill the gaps, otherwise the intervals again.
481
483{
484 // CONVENTION: _yatX[ixlo] is filled, _yatX[ixhi] is filled, elements in between are empty
485 // std::cout << "fillGap: gap from _yatX[" << ixlo << "]=" << _yatX[ixlo] << " to _yatX[" << ixhi << "]=" <<
486 // _yatX[ixhi] << ", size = " << ixhi-ixlo << std::endl ;
487
488 if (_yatX[ixlo] < 0) {
489 oocoutE(_self, Eval) << "RooIntegralMorph::MorphCacheElme::fillGap(" << _self->GetName() << "): ERROR in fillgap "
490 << ixlo << " = " << ixhi << " splitPoint= " << splitPoint << " _yatX[ixlo] = " << _yatX[ixlo]
491 << std::endl;
492 }
493 if (_yatX[ixhi] < 0) {
494 oocoutE(_self, Eval) << "RooIntegralMorph::MorphCacheElme::fillGap(" << _self->GetName() << "): ERROR in fillgap "
495 << ixlo << " = " << ixhi << " splitPoint " << splitPoint << " _yatX[ixhi] = " << _yatX[ixhi]
496 << std::endl;
497 }
498
499 // Determine where half-way Y value lands
500 double ymid = _yatX[ixlo] * splitPoint + _yatX[ixhi] * (1 - splitPoint);
501 bool ok;
502 double Xmid = calcX(ymid, ok);
503 if (!ok) {
504 oocoutW(_self, Eval) << "RooIntegralMorph::MorphCacheElem::fillGap(" << _self->GetName()
505 << ") unable to calculate midpoint in gap [" << ixlo << "," << ixhi
506 << "], resorting to interpolation" << std::endl;
507 interpolateGap(ixlo, ixhi);
508 }
509
510 Int_t iX = binX(Xmid);
511 double cq = (Xmid - _calcX[ixlo]) / (_calcX[ixhi] - _calcX[ixlo]) - 0.5;
512
513 // Store midway point
514 _yatX[iX] = ymid;
515 _calcX[iX] = Xmid;
516
517 // Policy: If centration quality is better than 1% OR better than 1/10 of a bin, fill interval with linear
518 // interpolation
519 if (std::abs(cq) < 0.01 || std::abs(cq * (ixhi - ixlo)) < 0.1 || ymid < _ycutoff) {
520
521 // Fill remaining gaps on either side with linear interpolation
522 if (iX - ixlo > 1) {
523 interpolateGap(ixlo, iX);
524 }
525 if (ixhi - iX > 1) {
526 interpolateGap(iX, ixhi);
527 }
528
529 } else {
530
531 if (iX == ixlo) {
532
533 if (splitPoint < 0.95) {
534 // Midway value lands on lowest bin, retry split with higher split point
535 double newSplit = splitPoint + 0.5 * (1 - splitPoint);
536 fillGap(ixlo, ixhi, newSplit);
537 } else {
538 // Give up and resort to interpolation
539 interpolateGap(ixlo, ixhi);
540 }
541
542 } else if (iX == ixhi) {
543
544 // Midway value lands on highest bin, retry split with lower split point
545 if (splitPoint > 0.05) {
546 double newSplit = splitPoint / 2;
547 fillGap(ixlo, ixhi, newSplit);
548 } else {
549 // Give up and resort to interpolation
550 interpolateGap(ixlo, ixhi);
551 }
552
553 } else {
554
555 // Midway point reasonable, iterate on interval on both sides
556 if (iX - ixlo > 1) {
557 fillGap(ixlo, iX);
558 }
559 if (ixhi - iX > 1) {
560 fillGap(iX, ixhi);
561 }
562 }
563 }
564}
565
566////////////////////////////////////////////////////////////////////////////////
567/// Fill empty histogram bins between ixlo and ixhi with values obtained
568/// from linear interpolation of ixlo,ixhi elements.
569
571{
572 // cout << "filling gap with linear interpolation ixlo=" << ixlo << " ixhi=" << ixhi << std::endl ;
573
574 double xmax = _x->getMax("cache");
575 double xmin = _x->getMin("cache");
576 double binw = (xmax - xmin) / _x->numBins("cache");
577
578 // Calculate deltaY in terms of actual X difference calculate, not based on nominal bin width
579 double deltaY = (_yatX[ixhi] - _yatX[ixlo]) / ((_calcX[ixhi] - _calcX[ixlo]) / binw);
580
581 // Calculate additional offset to apply if bin ixlo does not have X value calculated at bin center
582 double xBinC = xmin + (ixlo + 0.5) * binw;
583 double xOffset = xBinC - _calcX[ixlo];
584
585 for (int j = ixlo + 1; j < ixhi; j++) {
586 _yatX[j] = _yatX[ixlo] + (xOffset / binw + (j - ixlo)) * deltaY;
587 _calcX[j] = xmin + (j + 0.5) * binw;
588 }
589}
590
591////////////////////////////////////////////////////////////////////////////////
592/// Determine which range of y values can be mapped to x values
593/// from the numeric inversion of the input c.d.fs.
594/// Start with a y range of [0.1-0.9] and push boundaries
595/// outward with a factor of 1/sqrt(10). Stop iteration if
596/// inverted x values no longer change
597
599{
600 double xmin = _x->getMin("cache");
601 double xmax = _x->getMax("cache");
602 Int_t nbins = _x->numBins("cache");
603
604 double x1;
605 double x2;
606 bool ok = true;
607 double ymin = 0.1;
608 double yminSave(-1);
609 double Xsave(-1);
610 double Xlast = xmax;
611
612 // Find lowest Y value that can be measured
613 // Start at 0.1 and iteratively lower limit by sqrt(10)
614 while (true) {
615 ok &= _rf1->findRoot(x1, xmin, xmax, ymin);
616 ok &= _rf2->findRoot(x2, xmin, xmax, ymin);
617 oocxcoutD(_self, Eval) << "RooIntegralMorph::MorphCacheElem::findRange(" << _self->GetName()
618 << ") findMin: x1 = " << x1 << " x2 = " << x2 << " ok = " << (ok ? "T" : "F") << std::endl;
619
620 // Terminate in case of non-convergence
621 if (!ok)
622 break;
623
624 // Terminate if value of X no longer moves by >0.1 bin size
625 double X = _alpha->getVal() * x1 + (1 - _alpha->getVal()) * x2;
626 if (std::abs(X - Xlast) / (xmax - xmin) < 0.0001) {
627 break;
628 }
629 Xlast = X;
630
631 // Store new Y value
632 _yatXmin = (Int_t)(nbins * (X - xmin) / (xmax - xmin));
633 _yatX[_yatXmin] = ymin;
634 _calcX[_yatXmin] = X;
635 yminSave = ymin;
636 Xsave = X;
637
638 // Reduce ymin by half an order of magnitude
639 ymin /= sqrt(10.);
640
641 // Emergency break
642 if (ymin < _ycutoff)
643 break;
644 }
645 _yatX[_yatXmin] = yminSave;
646 _calcX[_yatXmin] = Xsave;
647
648 // Find highest Y value that can be measured
649 // Start at 1 - 0.1 and iteratively lower delta by sqrt(10)
650 ok = true;
651 double deltaymax = 0.1;
652 double deltaymaxSave(-1);
653 Xlast = xmin;
654 while (true) {
655 ok &= _rf1->findRoot(x1, xmin, xmax, 1 - deltaymax);
656 ok &= _rf2->findRoot(x2, xmin, xmax, 1 - deltaymax);
657
658 oocxcoutD(_self, Eval) << "RooIntegralMorph::MorphCacheElem::findRange(" << _self->GetName()
659 << ") findMax: x1 = " << x1 << " x2 = " << x2 << " ok = " << (ok ? "T" : "F") << std::endl;
660
661 // Terminate in case of non-convergence
662 if (!ok)
663 break;
664
665 // Terminate if value of X no longer moves by >0.1 bin size
666 double X = _alpha->getVal() * x1 + (1 - _alpha->getVal()) * x2;
667 if (std::abs(X - Xlast) / (xmax - xmin) < 0.0001) {
668 break;
669 }
670 Xlast = X;
671
672 // Store new Y value
673 _yatXmax = (Int_t)(nbins * (X - xmin) / (xmax - xmin));
674 _yatX[_yatXmax] = 1 - deltaymax;
675 _calcX[_yatXmax] = X;
677 Xsave = X;
678
679 // Reduce ymin by half an order of magnitude
680 deltaymax /= sqrt(10.);
681
682 // Emergency break
683 if (deltaymax < _ycutoff)
684 break;
685 }
686
687 _yatX[_yatXmax] = 1 - deltaymaxSave;
688 _calcX[_yatXmax] = Xsave;
689
690 // Initialize values out of range to 'out-of-range' (-2)
691 for (int i = 0; i < _yatXmin; i++)
692 _yatX[i] = -2;
693 for (int i = _yatXmax + 1; i < nbins; i++)
694 _yatX[i] = -2;
695 oocxcoutD(_self, Eval) << "RooIntegralMorph::findRange(" << _self->GetName() << "): ymin = " << _yatX[_yatXmin]
696 << " ymax = " << _yatX[_yatXmax] << std::endl;
697 oocxcoutD(_self, Eval) << "RooIntegralMorph::findRange(" << _self->GetName() << "): xmin = " << _calcX[_yatXmin]
698 << " xmax = " << _calcX[_yatXmax] << std::endl;
699}
700
701////////////////////////////////////////////////////////////////////////////////
702/// Dummy
703
705{
706 return 0;
707}
708
709////////////////////////////////////////////////////////////////////////////////
710/// Indicate to the RooAbsCachedPdf base class that for the filling of the
711/// cache the traversal of the x should be in the innermost loop, to minimize
712/// recalculation of the one-dimensional internal cache for a fixed value of alpha
713
715{
716 // Put x last to minimize cache faulting
717 orderedObs.removeAll();
718
719 orderedObs.add(obs);
720 RooAbsArg *obsX = obs.find(x.arg().GetName());
721 if (obsX) {
722 orderedObs.remove(*obsX);
723 orderedObs.add(*obsX);
724 }
725}
#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:60
#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:142
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 double offset() const
Definition RooAbsReal.h:368
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:77
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:137
Double_t y[n]
Definition legend1.C:17
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