Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
RooAbsOptTestStatistic.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 RooAbsOptTestStatistic.cxx
19 \class RooAbsOptTestStatistic
20 \ingroup Roofitcore
21 
22 RooAbsOptTestStatistic is the abstract base class for test
23 statistics objects that evaluate a function or PDF at each point of a given
24 dataset. This class provides generic optimizations, such as
25 caching and precalculation of constant terms that can be made for
26 all such quantities.
27 
28 Implementations should define evaluatePartition(), which calculates the
29 value of a (sub)range of the dataset and optionally combinedValue(),
30 which combines the values calculated for each partition. If combinedValue()
31 is not overloaded, the default implementation will add the partition results
32 to obtain the combined result.
33 
34 Support for calculation in partitions is needed to allow multi-core
35 parallelized calculation of test statistics.
36 **/
37 
38 #include "RooFit.h"
39 
40 #include "Riostream.h"
41 #include <string.h>
42 
43 
44 #include "RooAbsOptTestStatistic.h"
45 #include "RooMsgService.h"
46 #include "RooAbsPdf.h"
47 #include "RooAbsData.h"
48 #include "RooDataHist.h"
49 #include "RooArgSet.h"
50 #include "RooRealVar.h"
51 #include "RooErrorHandler.h"
52 #include "RooGlobalFunc.h"
53 #include "RooBinning.h"
54 #include "RooAbsDataStore.h"
55 #include "RooCategory.h"
56 #include "RooDataSet.h"
57 #include "RooProdPdf.h"
58 #include "RooAddPdf.h"
59 #include "RooProduct.h"
60 #include "RooRealSumPdf.h"
61 #include "RooTrace.h"
62 #include "RooVectorDataStore.h"
63 
64 using namespace std;
65 
66 ClassImp(RooAbsOptTestStatistic);
67 ;
68 
69 
70 ////////////////////////////////////////////////////////////////////////////////
71 /// Default Constructor
72 
73 RooAbsOptTestStatistic:: RooAbsOptTestStatistic()
74 {
75  // Initialize all non-persisted data members
76 
77  _funcObsSet = 0 ;
78  _funcCloneSet = 0 ;
79  _funcClone = 0 ;
80 
81  _normSet = 0 ;
82  _dataClone = 0 ;
83  _projDeps = 0 ;
84 
85  _origFunc = 0 ;
86  _origData = 0 ;
87 
88  _ownData = kTRUE ;
89  _sealed = kFALSE ;
90  _optimized = kFALSE ;
91 }
92 
93 
94 
95 ////////////////////////////////////////////////////////////////////////////////
96 /// Constructor taking function (real), a dataset (data), a set of projected observables (projSet). If
97 /// rangeName is not null, only events in the dataset inside the range will be used in the test
98 /// statistic calculation. If addCoefRangeName is not null, all RooAddPdf component of 'real' will be
99 /// instructed to fix their fraction definitions to the given named range. If nCPU is greater than
100 /// 1 the test statistic calculation will be paralellized over multiple processes. By default the data
101 /// is split with 'bulk' partitioning (each process calculates a contigious block of fraction 1/nCPU
102 /// of the data). For binned data this approach may be suboptimal as the number of bins with >0 entries
103 /// in each processing block many vary greatly thereby distributing the workload rather unevenly.
104 /// If interleave is set to true, the interleave partitioning strategy is used where each partition
105 /// i takes all bins for which (ibin % ncpu == i) which is more likely to result in an even workload.
106 /// If splitCutRange is true, a different rangeName constructed as rangeName_{catName} will be used
107 /// as range definition for each index state of a RooSimultaneous
108 
109 RooAbsOptTestStatistic::RooAbsOptTestStatistic(const char *name, const char *title, RooAbsReal& real, RooAbsData& indata,
110  const RooArgSet& projDeps, const char* rangeName, const char* addCoefRangeName,
111  Int_t nCPU, RooFit::MPSplit interleave, Bool_t verbose, Bool_t splitCutRange, Bool_t /*cloneInputData*/) :
112  RooAbsTestStatistic(name,title,real,indata,projDeps,rangeName, addCoefRangeName, nCPU, interleave, verbose, splitCutRange),
113  _projDeps(0),
114  _sealed(kFALSE),
115  _optimized(kFALSE)
116 {
117  // Don't do a thing in master mode
118 
119  if (operMode()!=Slave) {
120  _funcObsSet = 0 ;
121  _funcCloneSet = 0 ;
122  _funcClone = 0 ;
123  _normSet = 0 ;
124  _dataClone = 0 ;
125  _projDeps = 0 ;
126  _origFunc = 0 ;
127  _origData = 0 ;
128  _ownData = kFALSE ;
129  _sealed = kFALSE ;
130  return ;
131  }
132 
133  _origFunc = 0 ; //other._origFunc ;
134  _origData = 0 ; // other._origData ;
135 
136  initSlave(real,indata,projDeps,rangeName,addCoefRangeName) ;
137 }
138 
139 ////////////////////////////////////////////////////////////////////////////////
140 /// Copy constructor
141 
142 RooAbsOptTestStatistic::RooAbsOptTestStatistic(const RooAbsOptTestStatistic& other, const char* name) :
143  RooAbsTestStatistic(other,name), _sealed(other._sealed), _sealNotice(other._sealNotice), _optimized(kFALSE)
144 {
145  // Don't do a thing in master mode
146  if (operMode()!=Slave) {
147 
148  _funcObsSet = 0 ;
149  _funcCloneSet = 0 ;
150  _funcClone = 0 ;
151  _normSet = other._normSet ? ((RooArgSet*) other._normSet->snapshot()) : 0 ;
152  _dataClone = 0 ;
153  _projDeps = 0 ;
154  _origFunc = 0 ;
155  _origData = 0 ;
156  _ownData = kFALSE ;
157  return ;
158  }
159 
160  _origFunc = 0 ; //other._origFunc ;
161  _origData = 0 ; // other._origData ;
162  _projDeps = 0 ;
163 
164  initSlave(*other._funcClone,*other._dataClone,other._projDeps?*other._projDeps:RooArgSet(),other._rangeName.c_str(),other._addCoefRangeName.c_str()) ;
165 }
166 
167 
168 
169 ////////////////////////////////////////////////////////////////////////////////
170 
171 void RooAbsOptTestStatistic::initSlave(RooAbsReal& real, RooAbsData& indata, const RooArgSet& projDeps, const char* rangeName,
172  const char* addCoefRangeName)
173 {
174  RooArgSet obs(*indata.get()) ;
175  obs.remove(projDeps,kTRUE,kTRUE) ;
176 
177 
178  // ******************************************************************
179  // *** PART 1 *** Clone incoming pdf, attach to each other *
180  // ******************************************************************
181 
182  // Clone FUNC
183  _funcClone = (RooAbsReal*) real.cloneTree() ;
184  _funcCloneSet = 0 ;
185 
186  // Attach FUNC to data set
187  _funcObsSet = _funcClone->getObservables(indata) ;
188 
189  if (_funcClone->getAttribute("BinnedLikelihood")) {
190  _funcClone->setAttribute("BinnedLikelihoodActive") ;
191  }
192 
193  // Reattach FUNC to original parameters
194  RooArgSet* origParams = (RooArgSet*) real.getParameters(indata) ;
195  _funcClone->recursiveRedirectServers(*origParams) ;
196 
197  // Mark all projected dependents as such
198  if (projDeps.getSize()>0) {
199  RooArgSet *projDataDeps = (RooArgSet*) _funcObsSet->selectCommon(projDeps) ;
200  projDataDeps->setAttribAll("projectedDependent") ;
201  delete projDataDeps ;
202  }
203 
204  // If PDF is a RooProdPdf (with possible constraint terms)
205  // analyze pdf for actual parameters (i.e those in unconnected constraint terms should be
206  // ignored as here so that the test statistic will not be recalculated if those
207  // are changed
208  RooProdPdf* pdfWithCons = dynamic_cast<RooProdPdf*>(_funcClone) ;
209  if (pdfWithCons) {
210 
211  RooArgSet* connPars = pdfWithCons->getConnectedParameters(*indata.get()) ;
212  // Add connected parameters as servers
213  _paramSet.removeAll() ;
214  _paramSet.add(*connPars) ;
215  delete connPars ;
216 
217  } else {
218  // Add parameters as servers
219  _paramSet.add(*origParams) ;
220  }
221 
222 
223  delete origParams ;
224 
225  // Store normalization set
226  _normSet = (RooArgSet*) indata.get()->snapshot(kFALSE) ;
227 
228  // Expand list of observables with any observables used in parameterized ranges.
229  // This NEEDS to be a counting loop since we are inserting during the loop.
230  for (std::size_t i = 0; i < _funcObsSet->size(); ++i) {
231  auto realDepRLV = dynamic_cast<const RooAbsRealLValue*>((*_funcObsSet)[i]);
232  if (realDepRLV && realDepRLV->isDerived()) {
233  RooArgSet tmp2;
234  realDepRLV->leafNodeServerList(&tmp2, 0, kTRUE);
235  _funcObsSet->add(tmp2,kTRUE);
236  }
237  }
238 
239 
240 
241  // ******************************************************************
242  // *** PART 2 *** Clone and adjust incoming data, attach to PDF *
243  // ******************************************************************
244 
245  // Check if the fit ranges of the dependents in the data and in the FUNC are consistent
246  const RooArgSet* dataDepSet = indata.get() ;
247  for (const auto arg : *_funcObsSet) {
248 
249  // Check that both dataset and function argument are of type RooRealVar
250  RooRealVar* realReal = dynamic_cast<RooRealVar*>(arg) ;
251  if (!realReal) continue ;
252  RooRealVar* datReal = dynamic_cast<RooRealVar*>(dataDepSet->find(realReal->GetName())) ;
253  if (!datReal) continue ;
254 
255  // Check that range of observables in pdf is equal or contained in range of observables in data
256 
257  if (!realReal->getBinning().lowBoundFunc() && realReal->getMin()<(datReal->getMin()-1e-6)) {
258  coutE(InputArguments) << "RooAbsOptTestStatistic: ERROR minimum of FUNC observable " << arg->GetName()
259  << "(" << realReal->getMin() << ") is smaller than that of "
260  << arg->GetName() << " in the dataset (" << datReal->getMin() << ")" << endl ;
261  RooErrorHandler::softAbort() ;
262  return ;
263  }
264 
265  if (!realReal->getBinning().highBoundFunc() && realReal->getMax()>(datReal->getMax()+1e-6)) {
266  coutE(InputArguments) << "RooAbsOptTestStatistic: ERROR maximum of FUNC observable " << arg->GetName()
267  << " is larger than that of " << arg->GetName() << " in the dataset" << endl ;
268  RooErrorHandler::softAbort() ;
269  return ;
270  }
271 
272  }
273 
274  // Copy data and strip entries lost by adjusted fit range, _dataClone ranges will be copied from realDepSet ranges
275  if (rangeName && strlen(rangeName)) {
276  _dataClone = ((RooAbsData&)indata).reduce(RooFit::SelectVars(*_funcObsSet),RooFit::CutRange(rangeName)) ;
277 // cout << "RooAbsOptTestStatistic: reducing dataset to fit in range named " << rangeName << " resulting dataset has " << _dataClone->sumEntries() << " events" << endl ;
278  } else {
279  _dataClone = (RooAbsData*) indata.Clone() ;
280  }
281  _ownData = kTRUE ;
282 
283 
284  // ******************************************************************
285  // *** PART 3 *** Make adjustments for fit ranges, if specified *
286  // ******************************************************************
287 
288  RooArgSet* origObsSet = real.getObservables(indata) ;
289  RooArgSet* dataObsSet = (RooArgSet*) _dataClone->get() ;
290  if (rangeName && strlen(rangeName)) {
291  cxcoutI(Fitting) << "RooAbsOptTestStatistic::ctor(" << GetName() << ") constructing test statistic for sub-range named " << rangeName << endl ;
292  //cout << "now adjusting observable ranges to requested fit range" << endl ;
293 
294  // Adjust FUNC normalization ranges to requested fitRange, store original ranges for RooAddPdf coefficient interpretation
295  for (const auto arg : *_funcObsSet) {
296 
297  RooRealVar* realObs = dynamic_cast<RooRealVar*>(arg) ;
298  if (realObs) {
299 
300  // If no explicit range is given for RooAddPdf coefficients, create explicit named range equivalent to original observables range
301  if (!(addCoefRangeName && strlen(addCoefRangeName))) {
302  realObs->setRange(Form("NormalizationRangeFor%s",rangeName),realObs->getMin(),realObs->getMax()) ;
303 // cout << "RAOTS::ctor() setting range " << Form("NormalizationRangeFor%s",rangeName) << " on observable "
304 // << realObs->GetName() << " to [" << realObs->getMin() << "," << realObs->getMax() << "]" << endl ;
305  }
306 
307  // Adjust range of function observable to those of given named range
308  realObs->setRange(realObs->getMin(rangeName),realObs->getMax(rangeName)) ;
309 // cout << "RAOTS::ctor() setting normalization range on observable "
310 // << realObs->GetName() << " to [" << realObs->getMin() << "," << realObs->getMax() << "]" << endl ;
311 
312  // Adjust range of data observable to those of given named range
313  RooRealVar* dataObs = (RooRealVar*) dataObsSet->find(realObs->GetName()) ;
314  dataObs->setRange(realObs->getMin(rangeName),realObs->getMax(rangeName)) ;
315 
316  // Keep track of list of fit ranges in string attribute fit range of original p.d.f.
317  if (!_splitRange) {
318  const char* origAttrib = real.getStringAttribute("fitrange") ;
319  if (origAttrib) {
320  real.setStringAttribute("fitrange",Form("%s,fit_%s",origAttrib,GetName())) ;
321  } else {
322  real.setStringAttribute("fitrange",Form("fit_%s",GetName())) ;
323  }
324  RooRealVar* origObs = (RooRealVar*) origObsSet->find(arg->GetName()) ;
325  if (origObs) {
326  origObs->setRange(Form("fit_%s",GetName()),realObs->getMin(rangeName),realObs->getMax(rangeName)) ;
327  }
328  }
329 
330  }
331  }
332  }
333  delete origObsSet ;
334 
335  // If dataset is binned, activate caching of bins that are invalid because the're outside the
336  // updated range definition (WVE need to add virtual interface here)
337  RooDataHist* tmph = dynamic_cast<RooDataHist*>(_dataClone) ;
338  if (tmph) {
339  tmph->cacheValidEntries() ;
340  }
341 
342  // Fix RooAddPdf coefficients to original normalization range
343  if (rangeName && strlen(rangeName)) {
344 
345  // WVE Remove projected dependents from normalization
346  _funcClone->fixAddCoefNormalization(*_dataClone->get(),kFALSE) ;
347 
348  if (addCoefRangeName && strlen(addCoefRangeName)) {
349  cxcoutI(Fitting) << "RooAbsOptTestStatistic::ctor(" << GetName()
350  << ") fixing interpretation of coefficients of any RooAddPdf component to range " << addCoefRangeName << endl ;
351  _funcClone->fixAddCoefRange(addCoefRangeName,kFALSE) ;
352  } else {
353  cxcoutI(Fitting) << "RooAbsOptTestStatistic::ctor(" << GetName()
354  << ") fixing interpretation of coefficients of any RooAddPdf to full domain of observables " << endl ;
355  _funcClone->fixAddCoefRange(Form("NormalizationRangeFor%s",rangeName),kFALSE) ;
356  }
357  }
358 
359 
360  // This is deferred from part 2 - but must happen after part 3 - otherwise invalid bins cannot be properly marked in cacheValidEntries
361  _dataClone->attachBuffers(*_funcObsSet) ;
362  setEventCount(_dataClone->numEntries()) ;
363 
364 
365 
366 
367  // *********************************************************************
368  // *** PART 4 *** Adjust normalization range for projected observables *
369  // *********************************************************************
370 
371  // Remove projected dependents from normalization set
372  if (projDeps.getSize()>0) {
373 
374  _projDeps = (RooArgSet*) projDeps.snapshot(kFALSE) ;
375 
376  //RooArgSet* tobedel = (RooArgSet*) _normSet->selectCommon(*_projDeps) ;
377  _normSet->remove(*_projDeps,kTRUE,kTRUE) ;
378 
379  // Mark all projected dependents as such
380  RooArgSet *projDataDeps = (RooArgSet*) _funcObsSet->selectCommon(*_projDeps) ;
381  projDataDeps->setAttribAll("projectedDependent") ;
382  delete projDataDeps ;
383  }
384 
385 
386  coutI(Optimization) << "RooAbsOptTestStatistic::ctor(" << GetName() << ") optimizing internal clone of p.d.f for likelihood evaluation."
387  << "Lazy evaluation and associated change tracking will disabled for all nodes that depend on observables" << endl ;
388 
389 
390  // *********************************************************************
391  // *** PART 4 *** Finalization and activation of optimization *
392  // *********************************************************************
393 
394  //_origFunc = _func ;
395  //_origData = _data ;
396 
397  // Redirect pointers of base class to clone
398  _func = _funcClone ;
399  _data = _dataClone ;
400 
401  _funcClone->getVal(_normSet) ;
402 
403 // cout << "ROATS::ctor(" << GetName() << ") funcClone structure dump BEFORE opt" << endl ;
404 // _funcClone->Print("t") ;
405 
406  optimizeCaching() ;
407 
408 
409 // cout << "ROATS::ctor(" << GetName() << ") funcClone structure dump AFTER opt" << endl ;
410 // _funcClone->Print("t") ;
411 
412 }
413 
414 
415 ////////////////////////////////////////////////////////////////////////////////
416 /// Destructor
417 
418 RooAbsOptTestStatistic::~RooAbsOptTestStatistic()
419 {
420  if (operMode()==Slave) {
421  delete _funcClone ;
422  delete _funcObsSet ;
423  if (_projDeps) {
424  delete _projDeps ;
425  }
426  if (_ownData) {
427  delete _dataClone ;
428  }
429  }
430  delete _normSet ;
431 }
432 
433 
434 
435 ////////////////////////////////////////////////////////////////////////////////
436 /// Method to combined test statistic results calculated into partitions into
437 /// the global result. This default implementation adds the partition return
438 /// values
439 
440 Double_t RooAbsOptTestStatistic::combinedValue(RooAbsReal** array, Int_t n) const
441 {
442  // Default implementation returns sum of components
443  Double_t sum(0), carry(0);
444  for (Int_t i = 0; i < n; ++i) {
445  Double_t y = array[i]->getValV();
446  carry += reinterpret_cast<RooAbsOptTestStatistic*>(array[i])->getCarry();
447  y -= carry;
448  const Double_t t = sum + y;
449  carry = (t - sum) - y;
450  sum = t;
451  }
452  _evalCarry = carry;
453  return sum ;
454 }
455 
456 
457 
458 ////////////////////////////////////////////////////////////////////////////////
459 /// Catch server redirect calls and forward to internal clone of function
460 
461 Bool_t RooAbsOptTestStatistic::redirectServersHook(const RooAbsCollection& newServerList, Bool_t mustReplaceAll, Bool_t nameChange, Bool_t isRecursive)
462 {
463  RooAbsTestStatistic::redirectServersHook(newServerList,mustReplaceAll,nameChange,isRecursive) ;
464  if (operMode()!=Slave) return kFALSE ;
465  Bool_t ret = _funcClone->recursiveRedirectServers(newServerList,kFALSE,nameChange) ;
466  return ret ;
467 }
468 
469 
470 
471 ////////////////////////////////////////////////////////////////////////////////
472 /// Catch print hook function and forward to function clone
473 
474 void RooAbsOptTestStatistic::printCompactTreeHook(ostream& os, const char* indent)
475 {
476  RooAbsTestStatistic::printCompactTreeHook(os,indent) ;
477  if (operMode()!=Slave) return ;
478  TString indent2(indent) ;
479  indent2 += "opt >>" ;
480  _funcClone->printCompactTree(os,indent2.Data()) ;
481  os << indent2 << " dataset clone = " << _dataClone << " first obs = " << _dataClone->get()->first() << endl ;
482 }
483 
484 
485 
486 ////////////////////////////////////////////////////////////////////////////////
487 /// Driver function to propagate constant term optimizations in test statistic.
488 /// If code Activate is sent, constant term optimization will be executed.
489 /// If code Deactivate is sent, any existing constant term optimizations will
490 /// be abandoned. If codes ConfigChange or ValueChange are sent, any existing
491 /// constant term optimizations will be redone.
492 
493 void RooAbsOptTestStatistic::constOptimizeTestStatistic(ConstOpCode opcode, Bool_t doAlsoTrackingOpt)
494 {
495  // cout << "ROATS::constOpt(" << GetName() << ") funcClone structure dump BEFORE const-opt" << endl ;
496  // _funcClone->Print("t") ;
497 
498  RooAbsTestStatistic::constOptimizeTestStatistic(opcode,doAlsoTrackingOpt);
499  if (operMode()!=Slave) return ;
500 
501  if (_dataClone->hasFilledCache() && _dataClone->store()->cacheOwner()!=this) {
502  if (opcode==Activate) {
503  cxcoutW(Optimization) << "RooAbsOptTestStatistic::constOptimize(" << GetName()
504  << ") dataset cache is owned by another object, no constant term optimization can be applied" << endl ;
505  }
506  return ;
507  }
508 
509  if (!allowFunctionCache()) {
510  if (opcode==Activate) {
511  cxcoutI(Optimization) << "RooAbsOptTestStatistic::constOptimize(" << GetName()
512  << ") function caching prohibited by test statistic, no constant term optimization is applied" << endl ;
513  }
514  return ;
515  }
516 
517  if (_dataClone->hasFilledCache() && opcode==Activate) {
518  opcode=ValueChange ;
519  }
520 
521  switch(opcode) {
522  case Activate:
523  cxcoutI(Optimization) << "RooAbsOptTestStatistic::constOptimize(" << GetName()
524  << ") optimizing evaluation of test statistic by finding all nodes in p.d.f that depend exclusively"
525  << " on observables and constant parameters and precalculating their values" << endl ;
526  optimizeConstantTerms(kTRUE,doAlsoTrackingOpt) ;
527  break ;
528 
529  case DeActivate:
530  cxcoutI(Optimization) << "RooAbsOptTestStatistic::constOptimize(" << GetName()
531  << ") deactivating optimization of constant terms in test statistic" << endl ;
532  optimizeConstantTerms(kFALSE) ;
533  break ;
534 
535  case ConfigChange:
536  cxcoutI(Optimization) << "RooAbsOptTestStatistic::constOptimize(" << GetName()
537  << ") one ore more parameter were changed from constant to floating or vice versa, "
538  << "re-evaluating constant term optimization" << endl ;
539  optimizeConstantTerms(kFALSE) ;
540  optimizeConstantTerms(kTRUE,doAlsoTrackingOpt) ;
541  break ;
542 
543  case ValueChange:
544  cxcoutI(Optimization) << "RooAbsOptTestStatistic::constOptimize(" << GetName()
545  << ") the value of one ore more constant parameter were changed re-evaluating constant term optimization" << endl ;
546  // Request a forcible cache update of all cached nodes
547  _dataClone->store()->forceCacheUpdate() ;
548 
549  break ;
550  }
551 
552 // cout << "ROATS::constOpt(" << GetName() << ") funcClone structure dump AFTER const-opt" << endl ;
553 // _funcClone->Print("t") ;
554 }
555 
556 
557 
558 ////////////////////////////////////////////////////////////////////////////////
559 /// This method changes the value caching logic for all nodes that depends on any of the observables
560 /// as defined by the given dataset. When evaluating a test statistic constructed from the RooAbsReal
561 /// with a dataset the observables are guaranteed to change with every call, thus there is no point
562 /// in tracking these changes which result in a net overhead. Thus for observable-dependent nodes,
563 /// the evaluation mechanism is changed from being dependent on a 'valueDirty' flag to guaranteed evaluation.
564 /// On the dataset side, the observables objects are modified to no longer send valueDirty messages
565 /// to their client
566 
567 void RooAbsOptTestStatistic::optimizeCaching()
568 {
569 // cout << "RooAbsOptTestStatistic::optimizeCaching(" << GetName() << "," << this << ")" << endl ;
570 
571  // Trigger create of all object caches now in nodes that have deferred object creation
572  // so that cache contents can be processed immediately
573  _funcClone->getVal(_normSet) ;
574 
575  // Set value caching mode for all nodes that depend on any of the observables to ADirty
576  _funcClone->optimizeCacheMode(*_funcObsSet) ;
577 
578  // Disable propagation of dirty state flags for observables
579  _dataClone->setDirtyProp(kFALSE) ;
580 
581  // Disable reading of observables that are not used
582  _dataClone->optimizeReadingWithCaching(*_funcClone, RooArgSet(),requiredExtraObservables()) ;
583 }
584 
585 
586 
587 ////////////////////////////////////////////////////////////////////////////////
588 /// Driver function to activate global constant term optimization.
589 /// If activated, constant terms are found and cached with the dataset.
590 /// The operation mode of cached nodes is set to AClean meaning that
591 /// their getVal() call will never result in an evaluate call.
592 /// Finally the branches in the dataset that correspond to observables
593 /// that are exclusively used in constant terms are disabled as
594 /// they serve no more purpose
595 
596 void RooAbsOptTestStatistic::optimizeConstantTerms(Bool_t activate, Bool_t applyTrackingOpt)
597 {
598  if(activate) {
599 
600  if (_optimized) {
601  return ;
602  }
603 
604  // Trigger create of all object caches now in nodes that have deferred object creation
605  // so that cache contents can be processed immediately
606  _funcClone->getVal(_normSet) ;
607 
608  // Apply tracking optimization here. Default strategy is to track components
609  // of RooAddPdfs and RooRealSumPdfs. If these components are a RooProdPdf
610  // or a RooProduct respectively, track the components of these products instead
611  // of the product term
612  RooArgSet trackNodes ;
613 
614 
615  // Add safety check here - applyTrackingOpt will only be applied if present
616  // dataset is constructed in terms of a RooVectorDataStore
617  if (applyTrackingOpt) {
618  if (!dynamic_cast<RooVectorDataStore*>(_dataClone->store())) {
619  coutW(Optimization) << "RooAbsOptTestStatistic::optimizeConstantTerms(" << GetName()
620  << ") WARNING Cache-and-track optimization (Optimize level 2) is only available for datasets"
621  << " implement in terms of RooVectorDataStore - ignoring this option for current dataset" << endl ;
622  applyTrackingOpt = kFALSE ;
623  }
624  }
625 
626  if (applyTrackingOpt) {
627  RooArgSet branches ;
628  _funcClone->branchNodeServerList(&branches) ;
629  for (auto arg : branches) {
630  arg->setCacheAndTrackHints(trackNodes);
631  }
632  // Do not set CacheAndTrack on constant expressions
633  RooArgSet* constNodes = (RooArgSet*) trackNodes.selectByAttrib("Constant",kTRUE) ;
634  trackNodes.remove(*constNodes) ;
635  delete constNodes ;
636 
637  // Set CacheAndTrack flag on all remaining nodes
638  trackNodes.setAttribAll("CacheAndTrack",kTRUE) ;
639  }
640 
641  // Find all nodes that depend exclusively on constant parameters
642  _cachedNodes.removeAll() ;
643 
644  _funcClone->findConstantNodes(*_dataClone->get(),_cachedNodes) ;
645 
646 // cout << "ROATS::oCT(" << GetName() << ") funcClone structure dump BEFORE cacheArgs" << endl ;
647 // _funcClone->Print("t") ;
648 
649 
650  // Cache constant nodes with dataset - also cache entries corresponding to zero-weights in data when using BinnedLikelihood
651  _dataClone->cacheArgs(this,_cachedNodes,_normSet,!_funcClone->getAttribute("BinnedLikelihood")) ;
652 
653 // cout << "ROATS::oCT(" << GetName() << ") funcClone structure dump AFTER cacheArgs" << endl ;
654 // _funcClone->Print("t") ;
655 
656 
657  // Put all cached nodes in AClean value caching mode so that their evaluate() is never called
658  for (auto cacheArg : _cachedNodes) {
659  cacheArg->setOperMode(RooAbsArg::AClean) ;
660  }
661 
662 
663 // cout << "_cachedNodes = " << endl ;
664 // RooFIter i = _cachedNodes.fwdIterator() ;
665 // RooAbsArg* aa ;
666 // while ((aa=i.next())) {
667 // cout << aa->IsA()->GetName() << "::" << aa->GetName() << (aa->getAttribute("ConstantExpressionCached")?" CEC":" ") << (aa->getAttribute("CacheAndTrack")?" CAT":" ") << endl ;
668 // }
669 
670  RooArgSet* constNodes = (RooArgSet*) _cachedNodes.selectByAttrib("ConstantExpressionCached",kTRUE) ;
671  RooArgSet actualTrackNodes(_cachedNodes) ;
672  actualTrackNodes.remove(*constNodes) ;
673  if (constNodes->getSize()>0) {
674  if (constNodes->getSize()<20) {
675  coutI(Minimization) << " The following expressions have been identified as constant and will be precalculated and cached: " << *constNodes << endl ;
676  } else {
677  coutI(Minimization) << " A total of " << constNodes->getSize() << " expressions have been identified as constant and will be precalculated and cached." << endl ;
678  }
679 // RooFIter i = constNodes->fwdIterator() ;
680 // RooAbsArg* cnode ;
681 // while((cnode=i.next())) {
682 // cout << cnode->IsA()->GetName() << "::" << cnode->GetName() << endl ;
683 // }
684  }
685  if (actualTrackNodes.getSize()>0) {
686  if (actualTrackNodes.getSize()<20) {
687  coutI(Minimization) << " The following expressions will be evaluated in cache-and-track mode: " << actualTrackNodes << endl ;
688  } else {
689  coutI(Minimization) << " A total of " << constNodes->getSize() << " expressions will be evaluated in cache-and-track-mode." << endl ;
690  }
691  }
692  delete constNodes ;
693 
694  // Disable reading of observables that are no longer used
695  _dataClone->optimizeReadingWithCaching(*_funcClone, _cachedNodes,requiredExtraObservables()) ;
696 
697  _optimized = kTRUE ;
698 
699  } else {
700 
701  // Delete the cache
702  _dataClone->resetCache() ;
703 
704  // Reactivate all tree branches
705  _dataClone->setArgStatus(*_dataClone->get(),kTRUE) ;
706 
707  // Reset all nodes to ADirty
708  optimizeCaching() ;
709 
710  // Disable propagation of dirty state flags for observables
711  _dataClone->setDirtyProp(kFALSE) ;
712 
713  _cachedNodes.removeAll() ;
714 
715 
716  _optimized = kFALSE ;
717  }
718 }
719 
720 
721 
722 ////////////////////////////////////////////////////////////////////////////////
723 /// cout << "RAOTS::setDataSlave(" << this << ") START" << endl ;
724 /// Change dataset that is used to given one. If cloneData is kTRUE, a clone of
725 /// in the input dataset is made. If the test statistic was constructed with
726 /// a range specification on the data, the cloneData argument is ignore and
727 /// the data is always cloned.
728 
729 Bool_t RooAbsOptTestStatistic::setDataSlave(RooAbsData& indata, Bool_t cloneData, Bool_t ownNewData)
730 {
731 
732  if (operMode()==SimMaster) {
733  //cout << "ROATS::setDataSlave() ERROR this is SimMaster _funcClone = " << _funcClone << endl ;
734  return kFALSE ;
735  }
736 
737  //cout << "ROATS::setDataSlave() new dataset size = " << indata.numEntries() << endl ;
738  //indata.Print("v") ;
739 
740 
741  // Delete previous dataset now, if it was owned
742  if (_ownData) {
743  delete _dataClone ;
744  _dataClone = 0 ;
745  }
746 
747  if (!cloneData && _rangeName.size()>0) {
748  coutW(InputArguments) << "RooAbsOptTestStatistic::setData(" << GetName() << ") WARNING: test statistic was constructed with range selection on data, "
749  << "ignoring request to _not_ clone the input dataset" << endl ;
750  cloneData = kTRUE ;
751  }
752 
753  if (cloneData) {
754  // Cloning input dataset
755  if (_rangeName.size()==0) {
756  _dataClone = (RooAbsData*) indata.reduce(*indata.get()) ;
757  } else {
758  _dataClone = ((RooAbsData&)indata).reduce(RooFit::SelectVars(*indata.get()),RooFit::CutRange(_rangeName.c_str())) ;
759  }
760  _ownData = kTRUE ;
761 
762  } else {
763 
764  // Taking input dataset
765  _dataClone = &indata ;
766  _ownData = ownNewData ;
767 
768  }
769 
770  // Attach function clone to dataset
771  _dataClone->attachBuffers(*_funcObsSet) ;
772  _dataClone->setDirtyProp(kFALSE) ;
773  _data = _dataClone ;
774 
775  // ReCache constant nodes with dataset
776  if (_cachedNodes.getSize()>0) {
777  _dataClone->cacheArgs(this,_cachedNodes,_normSet) ;
778  }
779 
780  // Adjust internal event count
781  setEventCount(indata.numEntries()) ;
782 
783  setValueDirty() ;
784 
785 // cout << "RAOTS::setDataSlave(" << this << ") END" << endl ;
786 
787  return kTRUE ;
788 }
789 
790 
791 
792 
793 ////////////////////////////////////////////////////////////////////////////////
794 
795 RooAbsData& RooAbsOptTestStatistic::data()
796 {
797  if (_sealed) {
798  Bool_t notice = (sealNotice() && strlen(sealNotice())) ;
799  coutW(ObjectHandling) << "RooAbsOptTestStatistic::data(" << GetName()
800  << ") WARNING: object sealed by creator - access to data is not permitted: "
801  << (notice?sealNotice():"<no user notice>") << endl ;
802  static RooDataSet dummy ("dummy","dummy",RooArgSet()) ;
803  return dummy ;
804  }
805  return *_dataClone ;
806 }
807 
808 
809 ////////////////////////////////////////////////////////////////////////////////
810 
811 const RooAbsData& RooAbsOptTestStatistic::data() const
812 {
813  if (_sealed) {
814  Bool_t notice = (sealNotice() && strlen(sealNotice())) ;
815  coutW(ObjectHandling) << "RooAbsOptTestStatistic::data(" << GetName()
816  << ") WARNING: object sealed by creator - access to data is not permitted: "
817  << (notice?sealNotice():"<no user notice>") << endl ;
818  static RooDataSet dummy ("dummy","dummy",RooArgSet()) ;
819  return dummy ;
820  }
821  return *_dataClone ;
822 }
823 
824