Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RooUnitTest.cxx
Go to the documentation of this file.
1/// \cond ROOFIT_INTERNAL
2
3/*****************************************************************************
4 * Project: RooFit *
5 * Package: RooFitCore *
6 * @(#)root/roofitcore:$Id$
7 * Authors: *
8 * WV, Wouter Verkerke, NIKHEF, verkerke@nikhef.nl *
9 * *
10 * Copyright (c) 2000-2011, Regents of the University of California *
11 * and Stanford University. All rights reserved. *
12 * *
13 * Redistribution and use in source and binary forms, *
14 * with or without modification, are permitted according to the terms *
15 * listed in LICENSE (http://roofit.sourceforge.net/license.txt) *
16 *****************************************************************************/
17
18/**
19\file RooUnitTest.cxx
20\class RooUnitTest
21\ingroup Roofitcore
22
23RooUnit test is an abstract base class for unit regression tests for
24RooFit and RooStats tests performed in stressRooFit and stressRooStats
25Implementations of this class must implement abstract method testCode()
26which defines the regression test to be performed. Inside testCode()
27the regression test can define objects on which the regression is performed.
28These are:
29Object | function
30----------------|------------
31 RooPlot | regPlot()
32 RooFitResult | regResult()
33 double | regValue()
34 RooTable | regTable()
35 TH1/2/3 | regTH()
36 RooWorkspace | regWS()
37**/
38
39#include "RooUnitTest.h"
40
41#include "RooCurve.h"
42#include "RooHist.h"
43#include "RooMsgService.h"
44#include "RooDouble.h"
45#include "RooRandom.h"
46
47#include <TClass.h>
48#include <TDirectory.h>
49#include <TFile.h>
50
51#include <cmath>
52
53TDirectory* RooUnitTest::gMemDir = nullptr;
54
55
56////////////////////////////////////////////////////////////////////////////////
57
58RooUnitTest::RooUnitTest(const char* name, TFile* refFile, bool writeRef, Int_t verbose) : TNamed(name,name),
59 _refFile(refFile), _debug(false), _write(writeRef), _verb(verbose)
60{
61}
62
63
64////////////////////////////////////////////////////////////////////////////////
65
66void RooUnitTest::regPlot(RooPlot* frame, const char* refName)
67{
68 if (_refFile) {
69 std::string refNameStr(refName) ;
70 frame->SetName(refName) ;
71 // Since ROOT 6.28, the RooPlot doesn't clone the plot variable by default
72 // anymore. This is a problem for registering the RooPlots, because they
73 // need to survive without dangling pointers even after the RooUnitTest is
74 // done. For that reason, we ask the RooPlot to create an internal plot
75 // variable clone for itself.
77 _regPlots.emplace_back(frame,refNameStr);
78 } else {
79 delete frame ;
80 }
81}
82
83
84////////////////////////////////////////////////////////////////////////////////
85
86void RooUnitTest::regResult(std::unique_ptr<RooFitResult> r, const char* refName)
87{
88 if (_refFile) {
89 _regResults.emplace_back(r.release(),refName);
90 }
91}
92
93
94////////////////////////////////////////////////////////////////////////////////
95
96void RooUnitTest::regValue(double d, const char* refName)
97{
98 if (_refFile) {
99 _regValues.emplace_back(d,refName);
100 }
101}
102
103
104////////////////////////////////////////////////////////////////////////////////
105
106void RooUnitTest::regTable(RooTable* t, const char* refName)
107{
108 if (_refFile) {
109 _regTables.emplace_back(t,refName) ;
110 } else {
111 delete t ;
112 }
113}
114
115
116////////////////////////////////////////////////////////////////////////////////
117
118void RooUnitTest::regWS(RooWorkspace* ws, const char* refName)
119{
120 if (_refFile) {
121 _regWS.emplace_back(ws,refName) ;
122 } else {
123 delete ws ;
124 }
125}
126
127
128////////////////////////////////////////////////////////////////////////////////
129
130void RooUnitTest::regTH(TH1* th, const char* refName)
131{
132 if (_refFile) {
133 _regTH.emplace_back(th,refName) ;
134 } else {
135 delete th ;
136 }
137}
138
139
140////////////////////////////////////////////////////////////////////////////////
141
142RooWorkspace* RooUnitTest::getWS(const char* refName)
143{
144 RooWorkspace* ws = dynamic_cast<RooWorkspace*>(_refFile->Get(refName)) ;
145 if (!ws) {
146 if(_verb >= 0) std::cout << "RooUnitTest ERROR: cannot retrieve RooWorkspace " << refName
147 << " from reference file, skipping " << std::endl ;
148 return nullptr ;
149 }
150
151 return ws ;
152}
153
154
155////////////////////////////////////////////////////////////////////////////////
156
157bool RooUnitTest::areTHidentical(TH1* htest, TH1* href)
158{
159 if (htest->GetDimension() != href->GetDimension()) {
160 return false ;
161 }
162
163 // Use Kolmogorov distance as metric rather than probability
164 // because we expect histograms to be identical rather
165 // than drawn from the same parent distribution
166 double kmax = htest->KolmogorovTest(href,"M") ;
167
168 if (kmax>htol()) {
169
170 if(_verb >= 0) std::cout << "KS distances = " << kmax << std::endl;
171
172 Int_t ntest = htest->GetNbinsX() +2 ;
173 Int_t nref = href->GetNbinsX() +2 ;
174 if (htest->GetDimension()>1) {
175 ntest *= htest->GetNbinsY() + 2 ;
176 nref *= href->GetNbinsY() + 2 ;
177 }
178 if (htest->GetDimension()>2) {
179 ntest *= htest->GetNbinsZ() + 2 ;
180 nref *= href->GetNbinsZ() + 2 ;
181 }
182
183 if (ntest != nref) {
184 return false ;
185 }
186
187 for (Int_t i=0 ; i<ntest ; i++) {
188 if (std::abs(htest->GetBinContent(i)-href->GetBinContent(i))>htol()) {
189 if(_verb >= 0) std::cout << "htest[" << i << "] = " << htest->GetBinContent(i) << " href[" << i << "] = " << href->GetBinContent(i) << std::endl;
190 }
191 }
192
193 return false ;
194 }
195
196 return true ;
197}
198
199
200
201////////////////////////////////////////////////////////////////////////////////
202
203bool RooUnitTest::runCompTests()
204{
205 bool ret = true ;
206
207 auto iter = _regPlots.begin() ;
208 while (iter!=_regPlots.end()) {
209
210 if (!_write) {
211
212 // Comparison mode
213
214 // Retrieve benchmark
215 RooPlot* bmark = dynamic_cast<RooPlot*>(_refFile->Get(iter->second.c_str())) ;
216 if (!bmark) {
217 if(_verb >= 0) std::cout << "RooUnitTest ERROR: cannot retrieve RooPlot " << iter->second
218 << " from reference file, skipping " << std::endl;
219 ret = false ;
220 ++iter ;
221 continue ;
222 }
223
224 if (_verb > 0) {
225 std::cout << "comparing RooPlot " << iter->first << " to benchmark " << iter->second << " = " << bmark << std::endl ;
226 std::cout << "reference: " ; iter->first->Print() ;
227 std::cout << "benchmark: " ; bmark->Print() ;
228 }
229
230 RooPlot* compPlot = _debug ? iter->first->emptyClone(Form("%s_comparison",iter->first->GetName())) : nullptr ;
231 bool anyFail=false ;
232
233 Stat_t nItems = iter->first->numItems() ;
234 for (Stat_t i=0 ; i<nItems ; i++) {
235 // coverity[NULL_RETURNS]
236 TObject* obj = iter->first->getObject((Int_t)i) ;
237
238 // Retrieve corresponding object from reference frame
239 TObject* objRef = bmark->findObject(obj->GetName()) ;
240
241 if (!objRef) {
242 if(_verb >= 0) std::cout << "RooUnitTest ERROR: cannot retrieve reference object " << obj->GetName()
243 << " from benchmark RooPlot " << iter->second << ", skipping" << std::endl;
244 ret = false ;
245 break ;
246 }
247
248 // Histogram comparisons
249 if (obj->IsA()==RooHist::Class()) {
250 RooHist* testHist = static_cast<RooHist*>(obj) ;
251 RooHist* refHist = static_cast<RooHist*>(objRef) ;
252 if (!testHist->isIdentical(*refHist,htol(),_verb >= 0)) {
253 if(_verb >= 0) std::cout << "RooUnitTest ERROR: comparison of object " << obj->ClassName() << "::" << obj->GetName()
254 << " fails comparison with counterpart in reference RooPlot " << bmark->GetName() << std::endl;
255
256 if (compPlot) {
257 compPlot->addPlotable(static_cast<RooHist*>(testHist->Clone()),"P") ;
258 compPlot->getAttLine()->SetLineColor(kRed) ;
259 compPlot->getAttMarker()->SetMarkerColor(kRed) ;
260 compPlot->getAttLine()->SetLineWidth(1) ;
261
262 compPlot->addPlotable(static_cast<RooHist*>(refHist->Clone()),"P") ;
263 compPlot->getAttLine()->SetLineColor(kBlue) ;
264 compPlot->getAttMarker()->SetMarkerColor(kBlue) ;
265 compPlot->getAttLine()->SetLineWidth(1) ;
266 }
267
268 anyFail=true ;
269 ret = false ;
270 }
271 } else if (obj->IsA()==RooCurve::Class()) {
272 RooCurve* testCurve = static_cast<RooCurve*>(obj) ;
273 RooCurve* refCurve = static_cast<RooCurve*>(objRef) ;
274 if (!testCurve->isIdentical(*refCurve,ctol(),_verb >= 0)) {
275 if(_verb >= 0) std::cout << "RooUnitTest ERROR: comparison of object " << obj->ClassName() << "::" << obj->GetName()
276 << " fails comparison with counterpart in reference RooPlot " << bmark->GetName() << std::endl;
277
278 if (compPlot) {
279 compPlot->addPlotable(static_cast<RooCurve*>(testCurve->Clone())) ;
280 compPlot->getAttLine()->SetLineColor(kRed) ;
281 compPlot->getAttLine()->SetLineWidth(1) ;
282 compPlot->getAttLine()->SetLineStyle(kSolid) ;
283
284 compPlot->addPlotable(static_cast<RooCurve*>(refCurve->Clone())) ;
285 compPlot->getAttLine()->SetLineColor(kBlue) ;
286 compPlot->getAttLine()->SetLineWidth(1) ;
287 compPlot->getAttLine()->SetLineStyle(kDashed) ;
288 }
289
290 anyFail=true ;
291 ret = false ;
292 }
293
294 }
295
296 }
297
298 if (anyFail && compPlot) {
299 std::cout << "RooUnitTest INFO: writing comparison plot " << compPlot->GetName() << " of failed test to RooUnitTest_DEBUG.root" << std::endl ;
300 TFile fdbg("RooUnitTest_DEBUG.root","UPDATE") ;
301 compPlot->Write() ;
302 fdbg.Close() ;
303 } else {
304 delete compPlot ;
305 }
306
307 // Delete RooPlot when comparison is finished to avoid noise in leak checking
308 delete iter->first ;
309
310 } else {
311
312 // Writing mode
313
314 std::cout <<"RooUnitTest: Writing reference RooPlot " << iter->first << " as benchmark " << iter->second << std::endl ;
315 _refFile->cd() ;
316 iter->first->Write(iter->second.c_str()) ;
317 gMemDir->cd() ;
318 }
319
320 ++iter ;
321 }
322
323
324 auto iter2 = _regResults.begin() ;
325 while (iter2!=_regResults.end()) {
326
327 if (!_write) {
328
329 // Comparison mode
330
331 // Retrieve benchmark
332 RooFitResult* bmark = dynamic_cast<RooFitResult*>(_refFile->Get(iter2->second.c_str())) ;
333 if (!bmark) {
334 if(_verb >= 0) std::cout << "RooUnitTest ERROR: cannot retrieve RooFitResult "
335 << iter2->second << " from reference file, skipping " << std::endl ;
336 ++iter2 ;
337 ret = false ;
338 continue ;
339 }
340
341 if (_verb > 0) {
342 std::cout << "comparing RooFitResult " << iter2->first << " to benchmark " << iter2->second << " = " << bmark << std::endl ;
343 }
344
345 if (!iter2->first->isIdentical(*bmark,fptol(),fctol(),_verb >= 0)) {
346 if (_verb >= 0) {
347 std::cout << "RooUnitTest ERROR: comparison of object " << iter2->first->ClassName()
348 << "::" << iter2->first->GetName() << " from result " << iter2->second
349 << " fails comparison with counterpart in reference RooFitResult " << bmark->GetName() << std::endl;
350 }
351 ret = false ;
352 }
353
354 // Delete RooFitResult when comparison is finished to avoid noise in leak checking
355 delete iter2->first ;
356
357
358 } else {
359
360 // Writing mode
361
362 std::cout <<"RooUnitTest: Writing reference RooFitResult " << iter2->first << " as benchmark " << iter2->second << std::endl ;
363 _refFile->cd() ;
364 iter2->first->Write(iter2->second.c_str()) ;
365 gMemDir->cd() ;
366 }
367
368 ++iter2 ;
369 }
370
371 auto iter3 = _regValues.begin() ;
372 while (iter3!=_regValues.end()) {
373
374 if (!_write) {
375
376 // Comparison mode
377
378 // Retrieve benchmark
379 RooDouble* ref = dynamic_cast<RooDouble*>(_refFile->Get(iter3->second.c_str())) ;
380 if (!ref) {
381 if(_verb >= 0) std::cout << "RooUnitTest ERROR: cannot retrieve RooDouble " << iter3->second << " from reference file, skipping " << std::endl;
382 ++iter3 ;
383 ret = false ;
384 continue ;
385 }
386
387 if (_verb > 0) {
388 std::cout << "comparing value " << iter3->first << " to benchmark " << iter3->second << " = " << (double)(*ref) << std::endl ;
389 }
390
391 if (std::abs(iter3->first - (double)(*ref))>vtol() ) {
392 if(_verb >= 0) std::cout << "RooUnitTest ERROR: comparison of value " << iter3->first << " fails comparison with reference " << ref->GetName() << std::endl ;
393 ret = false ;
394 }
395
396
397 } else {
398
399 // Writing mode
400
401 std::cout <<"RooUnitTest: Writing reference double " << iter3->first << " as benchmark " << iter3->second << std::endl ;
402 _refFile->cd() ;
403 RooDouble* rd = new RooDouble(iter3->first) ;
404 rd->Write(iter3->second.c_str()) ;
405 gMemDir->cd() ;
406 }
407
408 ++iter3 ;
409 }
410
411
412 auto iter4 = _regTables.begin() ;
413 while (iter4!=_regTables.end()) {
414
415 if (!_write) {
416
417 // Comparison mode
418
419 // Retrieve benchmark
420 RooTable* bmark = dynamic_cast<RooTable*>(_refFile->Get(iter4->second.c_str())) ;
421 if (!bmark) {
422 if(_verb >= 0) std::cout << "RooUnitTest ERROR: cannot retrieve RooTable " << iter4->second << " from reference file, skipping " << std::endl ;
423 ++iter4 ;
424 ret = false ;
425 continue ;
426 }
427
428 if (_verb > 0) {
429 std::cout << "comparing RooTable " << iter4->first << " to benchmark " << iter4->second << " = " << bmark << std::endl ;
430 }
431
432 if (!iter4->first->isIdentical(*bmark, _verb >= 0)) {
433 if(_verb >= 0) std::cout << "RooUnitTest ERROR: comparison of object " << iter4->first->ClassName() << "::" << iter4->first->GetName()
434 << " fails comparison with counterpart in reference RooTable " << bmark->GetName() << std::endl ;
435 if (_verb > 0) {
436 iter4->first->Print("V");
437 bmark->Print("V");
438 }
439 ret = false;
440 }
441
442 // Delete RooTable when comparison is finished to avoid noise in leak checking
443 delete iter4->first ;
444
445
446 } else {
447
448 // Writing mode
449
450 std::cout <<"RooUnitTest: Writing reference RooTable " << iter4->first << " as benchmark " << iter4->second << std::endl ;
451 _refFile->cd() ;
452 iter4->first->Write(iter4->second.c_str()) ;
453 gMemDir->cd() ;
454 }
455
456 ++iter4 ;
457 }
458
459
460 auto iter5 = _regWS.begin() ;
461 while (iter5!=_regWS.end()) {
462
463 if (_write) {
464
465 // Writing mode
466
467 std::cout <<"RooUnitTest: Writing reference RooWorkspace " << iter5->first << " as benchmark " << iter5->second << std::endl ;
468 _refFile->cd() ;
469 iter5->first->Write(iter5->second.c_str()) ;
470 gMemDir->cd() ;
471 }
472
473 ++iter5 ;
474 }
475
476 /////////////////
477 auto iter6 = _regTH.begin() ;
478 while (iter6!=_regTH.end()) {
479
480 if (!_write) {
481
482 // Comparison mode
483
484 // Retrieve benchmark
485 TH1* bmark = dynamic_cast<TH1*>(_refFile->Get(iter6->second.c_str())) ;
486 if (!bmark) {
487 if(_verb >= 0) std::cout << "RooUnitTest ERROR: cannot retrieve TH1 " << iter6->second << " from reference file, skipping " << std::endl ;
488 ++iter6 ;
489 ret = false ;
490 continue ;
491 }
492
493 if (_verb > 0) {
494 std::cout << "comparing TH1 " << iter6->first << " to benchmark " << iter6->second << " = " << bmark << std::endl ;
495 }
496
497 if (!areTHidentical(iter6->first,bmark)) {
498 // coverity[NULL_RETURNS]
499 if(_verb >= 0) std::cout << "RooUnitTest ERROR: comparison of object " << iter6->first->ClassName() << "::" << iter6->first->GetName()
500 << " fails comparison with counterpart in reference TH1 " << bmark->GetName() << std::endl ;
501
502
503 if (_debug) {
504 std::cout << "RooUnitTest INFO: writing THx " << iter6->first->GetName() << " and " << bmark->GetName()
505 << " of failed test to RooUnitTest_DEBUG.root" << std::endl ;
506 TFile fdbg("RooUnitTest_DEBUG.root","UPDATE") ;
507 iter6->first->SetName(Form("%s_test",iter6->first->GetName())) ;
508 iter6->first->Write() ;
509 bmark->SetName(Form("%s_ref",bmark->GetName())) ;
510 bmark->Write() ;
511 fdbg.Close() ;
512 }
513
514 ret = false ;
515 }
516
517 // Delete TH1 when comparison is finished to avoid noise in leak checking
518 delete iter6->first ;
519
520
521 } else {
522
523 // Writing mode
524
525 std::cout <<"RooUnitTest: Writing reference TH1 " << iter6->first << " as benchmark " << iter6->second << std::endl ;
526 _refFile->cd() ;
527 iter6->first->Write(iter6->second.c_str()) ;
528 gMemDir->cd() ;
529 }
530
531 ++iter6 ;
532 }
533
534
535 /////////////////
536
537 return ret ;
538}
539
540
541////////////////////////////////////////////////////////////////////////////////
542
543void RooUnitTest::setSilentMode()
544{
545 RooMsgService::instance().setSilentMode(true) ;
546 for (Int_t i=0 ; i<RooMsgService::instance().numStreams() ; i++) {
547 if (RooMsgService::instance().getStream(i).minLevel<RooFit::ERROR) {
548 RooMsgService::instance().setStreamStatus(i,false) ;
549 }
550 }
551}
552
553
554////////////////////////////////////////////////////////////////////////////////
555
556void RooUnitTest::clearSilentMode()
557{
558 RooMsgService::instance().setSilentMode(false) ;
559 for (Int_t i=0 ; i<RooMsgService::instance().numStreams() ; i++) {
560 RooMsgService::instance().setStreamStatus(i,true) ;
561 }
562}
563
564
565
566////////////////////////////////////////////////////////////////////////////////
567
568bool RooUnitTest::runTest()
569{
570 gMemDir->cd() ;
571
572 if (_verb<2) {
573 setSilentMode() ;
574 } else {
575 std::cout << "*** Begin of output of Unit Test at normal verbosity *************" << std::endl ;
576 }
577
578 RooMsgService::instance().clearErrorCount() ;
579
580 // Reset random generator seed to make results independent of test ordering
581 gRandom->SetSeed(12345) ;
582 RooRandom::randomGenerator()->SetSeed(12345) ;
583
584 if (!testCode()) return false ;
585
586 if (_verb<2) {
588 } else {
589 std::cout << "*** End of output of Unit Test at normal verbosity ***************" << std::endl ;
590 }
591
592 if (RooMsgService::instance().errorCount()>0) {
593 if(_verb >= 0) std::cout << "RooUnitTest: ERROR messages were logged, failing test" << std::endl ;
594 return false ;
595 }
596
597 return runCompTests() ;
598}
599
600
601////////////////////////////////////////////////////////////////////////////////
602/// Set gMemDir to memDir
603
604void RooUnitTest::setMemDir(TDirectory* memDir) {
605 gMemDir = memDir ;
606}
607
608/// \endcond
#define d(i)
Definition RSha256.hxx:102
int Int_t
Signed integer 4 bytes (int)
Definition RtypesCore.h:60
@ kRed
Definition Rtypes.h:67
@ kBlue
Definition Rtypes.h:67
@ kDashed
Definition TAttLine.h:54
@ kSolid
Definition TAttLine.h:54
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 GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t r
char name[80]
Definition TGX11.cxx:148
R__EXTERN TRandom * gRandom
Definition TRandom.h:73
char * Form(const char *fmt,...)
Formats a string in a circular formatting buffer.
Definition TString.cxx:2570
const_iterator begin() const
const_iterator end() const
One-dimensional graphical representation of a real-valued function.
Definition RooCurve.h:36
static TClass * Class()
Minimal implementation of a TObject holding a double value.
Definition RooDouble.h:22
RooFitResult is a container class to hold the input and output of a PDF fit to a dataset.
Graphical representation of binned data based on the TGraphAsymmErrors class.
Definition RooHist.h:29
static TClass * Class()
static RooMsgService & instance()
Return reference to singleton instance.
Plot frame and a container for graphics objects within that frame.
Definition RooPlot.h:43
void SetName(const char *name) override
Set the name of the RooPlot to 'name'.
Definition RooPlot.cxx:1187
void createInternalPlotVarClone()
Replaces the pointer to the plot variable with a pointer to a clone of the plot variable that is owne...
Definition RooPlot.cxx:1401
static TRandom * randomGenerator()
Return a pointer to a singleton random-number generator implementation.
Definition RooRandom.cxx:47
Abstract interface for table objects.
Definition RooTable.h:32
Persistable container for RooFit projects.
Describe directory structure in memory.
Definition TDirectory.h:45
A file, usually with extension .root, that stores data and code in the form of serialized objects in ...
Definition TFile.h:130
TH1 is the base class of all histogram classes in ROOT.
Definition TH1.h:109
The TNamed class is the base class for all named ROOT classes.
Definition TNamed.h:29
Mother of all ROOT objects.
Definition TObject.h:42
virtual const char * GetName() const
Returns name of object.
Definition TObject.cxx:461
virtual const char * ClassName() const
Returns name of class to which the object belongs.
Definition TObject.cxx:226
virtual TClass * IsA() const
Definition TObject.h:248
virtual void SetSeed(ULong_t seed=0)
Set the random generator seed.
Definition TRandom.cxx:614