Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RooRealIntegral.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 RooRealIntegral.cxx
19\class RooRealIntegral
20\ingroup Roofitcore
21
22Performs hybrid numerical/analytical integrals of RooAbsReal objects.
23The class performs none of the actual integration, but only manages the logic
24of what variables can be integrated analytically, accounts for eventual jacobian
25terms and defines what numerical integrations needs to be done to complement the
26analytical integral.
27The actual analytical integrations (if any) are done in the PDF themselves, the numerical
28integration is performed in the various implementations of the RooAbsIntegrator base class.
29**/
30
31#include <RooRealIntegral.h>
32
34#include <RooAbsRealLValue.h>
35#include <RooConstVar.h>
36#include <RooDouble.h>
38#include <RooInvTransform.h>
39#include <RooMsgService.h>
40#include <RooNameReg.h>
41#include <RooNumIntConfig.h>
42#include <RooNumIntFactory.h>
43#include <RooRealBinding.h>
44#include <RooSuperCategory.h>
45#include <RooFitImplHelpers.h>
46
47#include <iostream>
48#include <memory>
49
50
51namespace {
52
53/// Utility function that returns true if 'object server' is a server
54/// to exactly one of the RooAbsArgs in 'exclLVBranches'
56{
57 // Determine if given server serves exclusively exactly one of the given nodes in exclLVBranches
58
59 // Special case, no LV servers available
60 if (exclLVBranches.empty())
61 return false;
62
63 // If server has no clients and is not an LValue itself, return false
64 if (!server->hasClients() && exclLVBranches.find(server->GetName())) {
65 return false;
66 }
67
68 // WVE must check for value relations only here!!!!
69
70 // Loop over all clients
72 for (const auto client : server->valueClients()) {
73 // If client is not an LValue, recurse
74 if (!(exclLVBranches.find(client->GetName()) == client)) {
75 if (allBranches.find(client->GetName()) == client) {
77 // Client is a non-LValue that doesn't have an exclusive LValue server
78 return false;
79 }
80 }
81 } else {
82 // Client is an LValue
83 numLVServ++;
84 }
85 }
86
87 return (numLVServ == 1);
88}
89
90struct ServerToAdd {
91 ServerToAdd(RooAbsArg *theArg, bool isShape) : arg{theArg}, isShapeServer{isShape} {}
92 RooAbsArg *arg = nullptr;
93 bool isShapeServer = false;
94};
95
96void addObservableToServers(RooAbsReal const &function, RooAbsArg &leaf, std::vector<ServerToAdd> &serversToAdd,
97 const char *rangeName)
98{
99 auto leaflv = dynamic_cast<RooAbsRealLValue *>(&leaf);
100 if (leaflv && leaflv->getBinning(rangeName).isParameterized()) {
101 oocxcoutD(&function, Integration)
102 << function.GetName() << " : Observable " << leaf.GetName()
103 << " has parameterized binning, add value dependence of boundary objects rather than shape of leaf"
104 << std::endl;
105 if (leaflv->getBinning(rangeName).lowBoundFunc()) {
106 serversToAdd.emplace_back(leaflv->getBinning(rangeName).lowBoundFunc(), false);
107 }
108 if (leaflv->getBinning(rangeName).highBoundFunc()) {
109 serversToAdd.emplace_back(leaflv->getBinning(rangeName).highBoundFunc(), false);
110 }
111 } else {
112 oocxcoutD(&function, Integration) << function.GetName() << ": Adding observable " << leaf.GetName()
113 << " as shape dependent" << std::endl;
114 serversToAdd.emplace_back(&leaf, true);
115 }
116}
117
118void addParameterToServers(RooAbsReal const &function, RooAbsArg &leaf, std::vector<ServerToAdd> &serversToAdd,
119 bool isShapeServer)
120{
121 if (!isShapeServer) {
122 oocxcoutD(&function, Integration) << function.GetName() << ": Adding parameter " << leaf.GetName()
123 << " as value dependent" << std::endl;
124 } else {
125 oocxcoutD(&function, Integration) << function.GetName() << ": Adding parameter " << leaf.GetName()
126 << " as shape dependent" << std::endl;
127 }
128 serversToAdd.emplace_back(&leaf, isShapeServer);
129}
130
131enum class MarkedState { Dependent, Independent, AlreadyAdded };
132
133/// Mark all args that recursively are value clients of "dep".
134void unmarkDepValueClients(RooAbsArg const &dep, RooArgSet const &args, std::vector<MarkedState> &marked)
135{
136 assert(args.size() == marked.size());
137 auto index = args.index(dep);
138 if (index >= 0) {
139 marked[index] = MarkedState::Dependent;
140 for (RooAbsArg *client : dep.valueClients()) {
141 unmarkDepValueClients(*client, args, marked);
142 }
143 }
144}
145
146std::vector<ServerToAdd>
147getValueAndShapeServers(RooAbsReal const &function, RooArgSet const &depList, const char *rangeName)
148{
149 std::vector<ServerToAdd> serversToAdd;
150
151 // Get the full computation graph and sort it topologically
153 function.treeNodeServerList(&allArgsList, nullptr, true, true, /*valueOnly=*/false, false);
155 allArgs.sortTopologically();
156
157 // Figure out what are all the value servers only
159 function.treeNodeServerList(&allValueArgsList, nullptr, true, true, /*valueOnly=*/true, false);
161
162 // All "marked" args will be added as value servers to the integral
163 std::vector<MarkedState> marked(allArgs.size(), MarkedState::Independent);
164 marked.back() = MarkedState::Dependent; // We don't want to consider the function itself
165
166 // Mark all args that are (indirect) value servers of the integration
167 // variable or the integration variable itself. If something was marked,
168 // it means the integration variable was in the compute graph and we will
169 // add it to the server list.
170 for (RooAbsArg *dep : depList) {
171 if (RooAbsArg *depInArgs = allArgs.find(dep->GetName())) {
174 }
175 }
176
177 // We are adding all independent direct servers of the args depending on the
178 // integration variables
179 for (std::size_t i = 0; i < allArgs.size(); ++i) {
180 if (marked[i] == MarkedState::Dependent) {
181 for (RooAbsArg *server : allArgs[i]->servers()) {
182 int index = allArgs.index(server->GetName());
183 if (index >= 0 && marked[index] == MarkedState::Independent) {
185 marked[index] = MarkedState::AlreadyAdded;
186 }
187 }
188 }
189 }
190
191 return serversToAdd;
192}
193
195 const RooArgSet &allBranches)
196{
197 // If any of the branches in the computation graph of the function depend on
198 // the integrated variable, we can't do analytical integration. The only
199 // case where this would work is if the branch is an l-value with known
200 // Jacobian, but this case is already handled in step B) in the constructor
201 // by reexpressing the original integration variables in terms of
202 // higher-order l-values if possible.
204 for (RooAbsArg *intDep : intDeps) {
205 bool depOK = true;
206 for (RooAbsArg *branch : allBranches) {
207 // It's ok if the branch is the integration variable itself
208 if (intDep->namePtr() != branch->namePtr() && branch->dependsOnValue(*intDep)) {
209 depOK = false;
210 }
211 if (!depOK) break;
212 }
213 if (depOK) {
215 }
216 }
217
218 for (const auto arg : function.servers()) {
219
220 // Dependent or parameter?
221 if (!arg->dependsOnValue(filteredIntDeps)) {
222 continue;
223 } else if (!arg->isValueServer(function) && !arg->isShapeServer(function)) {
224 // Skip arg if it is neither value or shape server
225 continue;
226 }
227
228 bool depOK(false);
229 // Check for integratable AbsRealLValue
230
231 if (arg->isDerived()) {
232 RooAbsRealLValue *realArgLV = dynamic_cast<RooAbsRealLValue *>(arg);
233 RooAbsCategoryLValue *catArgLV = dynamic_cast<RooAbsCategoryLValue *>(arg);
234 if ((realArgLV && filteredIntDeps.find(realArgLV->GetName()) &&
235 (realArgLV->isJacobianOK(filteredIntDeps) != 0)) ||
236 catArgLV) {
237
238 // Derived LValue with valid jacobian
239 depOK = true;
240
241 // Now, check for overlaps
242 bool overlapOK = true;
243 for (const auto otherArg : function.servers()) {
244 // skip comparison with self
245 if (arg == otherArg)
246 continue;
247 if (dynamic_cast<RooConstVar const *>(otherArg))
248 continue;
249 if (arg->overlaps(*otherArg, true)) {
250 }
251 }
252 // coverity[DEADCODE]
253 if (!overlapOK)
254 depOK = false;
255 }
256 } else {
257 // Fundamental types are always OK
258 depOK = true;
259 }
260
261 // Add server to list of dependents that are OK for analytical integration
262 if (depOK) {
263 anIntOKDepList.add(*arg, true);
264 oocxcoutI(&function, Integration)
265 << function.GetName() << ": Observable " << arg->GetName()
266 << " is suitable for analytical integration (if supported by p.d.f)" << std::endl;
267 }
268 }
269}
270
271} // namespace
272
274
275////////////////////////////////////////////////////////////////////////////////
276
280
281////////////////////////////////////////////////////////////////////////////////
282/// Construct integral of 'function' over observables in 'depList'
283/// in range 'rangeName' with normalization observables 'funcNormSet'
284/// (for p.d.f.s). In the integral is performed to the maximum extent
285/// possible the internal (analytical) integrals advertised by function.
286/// The other integrations are performed numerically. The optional
287/// config object prescribes how these numeric integrations are configured.
288///
289/// \note If pdf component selection was globally overridden to always include
290/// all components (either with RooAbsReal::globalSelectComp(bool) or a
291/// RooAbsReal::GlobalSelectComponentRAII), then any created integral will
292/// ignore component selections during its lifetime. This is especially useful
293/// when creating normalization or projection integrals.
294RooRealIntegral::RooRealIntegral(const char *name, const char *title,
295 const RooAbsReal& function, const RooArgSet& depList,
296 const RooArgSet* funcNormSet, const RooNumIntConfig* config,
297 const char* rangeName) :
298 RooAbsReal(name,title),
299 _valid(true),
300 _respectCompSelect{!_globalSelectComp},
301 _sumList("!sumList","Categories to be summed numerically",this,false,false),
302 _intList("!intList","Variables to be integrated numerically",this,false,false),
303 _anaList("!anaList","Variables to be integrated analytically",this,false,false),
304 _jacList("!jacList","Jacobian product term",this,false,false),
305 _facList("!facList","Variables independent of function",this,false,true),
306 _function("!func","Function to be integrated",this,false,false),
307 _iconfig(const_cast<RooNumIntConfig*>(config)),
308 _sumCat("!sumCat","SuperCategory for summation",this,false,false),
309 _rangeName(const_cast<TNamed*>(RooNameReg::ptr(rangeName)))
310{
311 // A) Check that all dependents are lvalues
312 //
313 // B) Check if list of dependents can be re-expressed in
314 // lvalues that are higher in the expression tree
315 //
316 // C) Check for dependents that the PDF insists on integrating
317 // analytically itself
318 //
319 // D) Make list of servers that can be integrated analytically
320 // Add all parameters/dependents as value/shape servers
321 //
322 // E) Interact with function to make list of objects actually integrated analytically
323 //
324 // F) Make list of numerical integration variables consisting of:
325 // - Category dependents of RealLValues in analytical integration
326 // - Leaf nodes server lists of function server that are not analytically integrated
327 // - Make Jacobian list for analytically integrated RealLValues
328 //
329 // G) Split numeric list in integration list and summation list
330 //
331
332 oocxcoutI(&function,Integration) << "RooRealIntegral::ctor(" << GetName() << ") Constructing integral of function "
333 << function.GetName() << " over observables" << depList << " with normalization "
334 << (funcNormSet?*funcNormSet:RooArgSet()) << " with range identifier "
335 << (rangeName?rangeName:"<none>") << std::endl ;
336
337
338 // Choose same expensive object cache as integrand
340// std::cout << "RRI::ctor(" << GetName() << ") setting expensive object cache to " << &expensiveObjectCache() << " as taken from " << function.GetName() << std::endl ;
341
342 // Use objects integrator configuration if none is specified
343 if (!_iconfig) _iconfig = const_cast<RooNumIntConfig*>(function.getIntegratorConfig());
344
345 // Save private copy of funcNormSet, if supplied, excluding factorizing terms
346 if (funcNormSet) {
347 _funcNormSet = std::make_unique<RooArgSet>();
348 for (const auto nArg : *funcNormSet) {
349 if (function.dependsOn(*nArg)) {
350 _funcNormSet->addClone(*nArg) ;
351 }
352 }
353 }
354
355 //_funcNormSet = funcNormSet ? (RooArgSet*)funcNormSet->snapshot(false) : 0 ;
356
357 // Make internal copy of dependent list
359
360 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
361 // * A) Check that all dependents are lvalues and filter out any
362 // dependents that the PDF doesn't explicitly depend on
363 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
364
365 for (auto arg : intDepList) {
366 if(!arg->isLValue()) {
367 coutE(InputArguments) << ClassName() << "::" << GetName() << ": cannot integrate non-lvalue ";
368 arg->Print("1");
369 _valid= false;
370 }
371 if (!function.dependsOn(*arg)) {
372 std::unique_ptr<RooAbsArg> argClone{static_cast<RooAbsArg*>(arg->Clone())};
374 addOwnedComponents(std::move(argClone));
375 }
376 }
377
378 if (!_facList.empty()) {
379 oocxcoutI(&function,Integration) << function.GetName() << ": Factorizing observables are " << _facList << std::endl ;
380 }
381
382
383 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
384 // * B) Check if list of dependents can be re-expressed in *
385 // * lvalues that are higher in the expression tree *
386 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
387
388
389 // Initial fill of list of LValue branches
390 RooArgSet exclLVBranches("exclLVBranches") ;
393
395 function.treeNodeServerList(&branchListVDAll,nullptr,true,false,/*valueOnly=*/true);
396 // The branchListVD is similar to branchList but considers only value
397 // dependence, and we want to exclude the function itself
399 branchListVD.reserve(branchListVDAll.size());
401 if (branch != &function) {
402 // The branchListVDAll is a RooArgList, so it's not de-duplicated yet.
403 // Add elements to the branchListVD with the "silent" flag, so it
404 // de-duplicates while adding without printing errors.
405 branchListVD.add(*branch, /*silent=*/true);
406 }
407 }
408
409 for (auto branch: branchList) {
412 if ((realArgLV && (realArgLV->isJacobianOK(intDepList)!=0)) || catArgLV) {
413 exclLVBranches.add(*branch) ;
414 }
415 }
416 exclLVBranches.remove(depList,true,true) ;
417
418 // Initial fill of list of LValue leaf servers (put in intDepList, but the
419 // instances that are in the actual computation graph of the function)
420 RooArgSet exclLVServers("exclLVServers") ;
422
423 // Obtain mutual exclusive dependence by iterative reduction
424 bool converged(false) ;
425 while(!converged) {
427
428 // Reduce exclLVServers to only those serving exclusively exclLVBranches
429 std::vector<RooAbsArg*> toBeRemoved;
430 for (auto server : exclLVServers) {
432 toBeRemoved.push_back(server);
434 }
435 }
437
438 // Reduce exclLVBranches to only those depending exclusively on exclLVservers
439 // Attention: counting loop, since erasing from container
440 for (std::size_t i=0; i < exclLVBranches.size(); ++i) {
441 const RooAbsArg* branch = exclLVBranches[i];
443 branch->getObservables(&intDepList, brDepList);
444 RooArgSet bsList(brDepList,"bsList") ;
445 bsList.remove(exclLVServers,true,true) ;
446 if (!bsList.empty()) {
447 exclLVBranches.remove(*branch,true,true) ;
448 --i;
450 }
451 }
452 }
453
454 // Eliminate exclLVBranches that do not depend on any LVServer
455 // Attention: Counting loop, since modifying container
456 for (std::size_t i=0; i < exclLVBranches.size(); ++i) {
457 const RooAbsArg* branch = exclLVBranches[i];
458 if (!branch->dependsOnValue(exclLVServers)) {
459 exclLVBranches.remove(*branch,true,true) ;
460 --i;
461 }
462 }
463
464 // Replace exclusive lvalue branch servers with lvalue branches
465 // WVE Don't do this for binned distributions - deal with this using numeric integration with transformed bin boundaries
466 if (!exclLVServers.empty() && !function.isBinnedDistribution(exclLVBranches)) {
467 intDepList.remove(exclLVServers) ;
469 }
470
471
472 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
473 // * C) Check for dependents that the PDF insists on integrating *
474 // analytically itself *
475 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
476
478 for (auto arg : intDepList) {
479 if (function.forceAnalyticalInt(*arg)) {
480 anIntOKDepList.add(*arg) ;
481 }
482 }
483
484 if (!anIntOKDepList.empty()) {
485 oocxcoutI(&function,Integration) << function.GetName() << ": Observables that function forcibly requires to be integrated internally " << anIntOKDepList << std::endl ;
486 }
487
488
489 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
490 // * D) Make list of servers that can be integrated analytically *
491 // Add all parameters/dependents as value/shape servers *
492 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
493
496 // We will not add the servers just now, because it makes only sense to add
497 // them once we have made sure that this integral is not operating in
498 // pass-through mode. It will be done at the end of this constructor.
499
500 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
501 // * E) interact with function to make list of objects actually integrated analytically *
502 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
503
505
506 // Avoid confusion -- if mode is zero no analytical integral is defined regardless of contents of _anaList
507 if (_mode==0) {
509 }
510
511 if (_mode!=0) {
512 oocxcoutI(&function,Integration) << function.GetName() << ": Function integrated observables " << _anaList << " internally with code " << _mode << std::endl ;
513 }
514
515 // WVE kludge: synchronize dset for use in analyticalIntegral
516 // LM : I think this is needed only if _funcNormSet is not an empty set
517 if (_funcNormSet && !_funcNormSet->empty()) {
518 function.getVal(_funcNormSet.get()) ;
519 }
520
521 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
522 // * F) Make list of numerical integration variables consisting of: *
523 // * - Category dependents of RealLValues in analytical integration *
524 // * - Expanded server lists of server that are not analytically integrated *
525 // * Make Jacobian list with analytically integrated RealLValues *
526 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
527
528 // Loop over actually analytically integrated dependents
529 for (const auto arg : _anaList) {
530
531 // Process only derived RealLValues
532 if (dynamic_cast<RooAbsRealLValue const *>(arg) && arg->isDerived() && !arg->isFundamental()) {
533
534 // Add to list of Jacobians to calculate
535 _jacList.add(*arg) ;
536
537 // Add category dependent of LValueReal used in integration
538 std::unique_ptr<RooArgSet> argDepList{arg->getObservables(&intDepList)};
539 for (const auto argDep : *argDepList) {
540 if (dynamic_cast<RooAbsCategoryLValue const *>(argDep) && intDepList.contains(*argDep)) {
542 }
543 }
544 }
545 }
546
547
548 // If nothing was integrated analytically, swap back LVbranches for LVservers for subsequent numeric integration
549 if (_anaList.empty()) {
550 if (!exclLVServers.empty()) {
551 //cout << "NUMINT phase analList is empty. exclLVServers = " << exclLVServers << std::endl ;
552 intDepList.remove(exclLVBranches) ;
554 }
555 }
556 //cout << "NUMINT intDepList = " << intDepList << std::endl ;
557
558 // Loop again over function servers to add remaining numeric integrations
559 for (const auto arg : function.servers()) {
560
561 // Process only servers that are not treated analytically
562 if (!_anaList.find(arg->GetName()) && arg->dependsOn(intDepList)) {
563
564 // Process only derived RealLValues
565 if (dynamic_cast<RooAbsLValue*>(arg) && arg->isDerived() && intDepList.contains(*arg)) {
566 addNumIntDep(*arg) ;
567 } else {
568
569 // WVE this will only get the observables, but not l-value transformations
570 // Expand server in final dependents
571 auto argDeps = std::unique_ptr<RooArgSet>(arg->getObservables(&intDepList));
572
573 // Add final dependents, that are not forcibly integrated analytically,
574 // to numerical integration list
575 for (const auto dep : *argDeps) {
576 if (!_anaList.find(dep->GetName())) {
578 }
579 }
580 }
581 }
582 }
583
584 if (!_anaList.empty()) {
585 oocxcoutI(&function,Integration) << function.GetName() << ": Observables " << _anaList << " are analytically integrated with code " << _mode << std::endl ;
586 }
587 if (!_intList.empty()) {
588 oocxcoutI(&function,Integration) << function.GetName() << ": Observables " << _intList << " are numerically integrated" << std::endl ;
589 }
590 if (!_sumList.empty()) {
591 oocxcoutI(&function,Integration) << function.GetName() << ": Observables " << _sumList << " are numerically summed" << std::endl ;
592 }
593
594
595 // Determine operating mode
596 if (!_intList.empty() || !_sumList.empty()) {
597 // Numerical and optional Analytical integration
599 } else if (!_anaList.empty()) {
600 // Purely analytical integration
602 } else {
603 // No integration performed, where the function is a direct value server
605 _function._valueServer = true;
606 }
607 // We are only setting the function proxy now that it's clear if it's a value
608 // server or not.
609 _function.setArg(const_cast<RooAbsReal&>(function));
610
611 // Determine auto-dirty status
613
614 // Create value caches for _intList and _sumList
617
618
619 if (!_sumList.empty()) {
620 _sumCat.addOwned(std::make_unique<RooSuperCategory>(Form("%s_sumCat",GetName()),"sumCat",_sumList));
621 }
622
623 // Only if we are not in pass-through mode we need to add the shape and value
624 // servers separately.
626 for(auto const& toAdd : serversToAdd) {
627 addServer(*toAdd.arg, !toAdd.isShapeServer, toAdd.isShapeServer);
628 }
629 }
630
631}
632
633////////////////////////////////////////////////////////////////////////////////
634/// Set appropriate cache operation mode for integral depending on cache operation
635/// mode of server objects
636
638{
639 // If any of our servers are is forcedDirty or a projectedDependent, then we need to be ADirty
640 for (const auto server : _serverList) {
641 if (server->isValueServer(*this)) {
643 server->leafNodeServerList(&leafSet) ;
644 for (const auto leaf : leafSet) {
645 if (leaf->operMode()==ADirty && leaf->isValueServer(*this)) {
647 break ;
648 }
649 if (leaf->getAttribute("projectedDependent")) {
651 break ;
652 }
653 }
654 }
655 }
656}
657
658////////////////////////////////////////////////////////////////////////////////
659/// (Re)Initialize numerical integration engine if necessary. Return true if
660/// successful, or otherwise false.
661
663{
664 // if we already have an engine, check if it still works for the present limits.
665 if(_numIntEngine) {
666 if(_numIntEngine->isValid() && _numIntEngine->checkLimits() && !_restartNumIntEngine ) return true;
667 // otherwise, cleanup the old engine
668 _numIntEngine.reset();
669 _numIntegrand.reset();
670 }
671
672 // All done if there are no arguments to integrate numerically
673 if(_intList.empty()) return true;
674
675 // Bind the appropriate analytic integral of our RooRealVar object to
676 // those of its arguments that will be integrated out numerically.
677 if(_mode != 0) {
679 _numIntegrand = std::make_unique<RooRealBinding>(*analyticalPart,_intList,nullptr,false,_rangeName);
680 const_cast<RooRealIntegral*>(this)->addOwnedComponents(std::move(analyticalPart));
681 }
682 else {
683 _numIntegrand = std::make_unique<RooRealBinding>(*_function,_intList,actualFuncNormSet(),false,_rangeName);
684 }
685 if(nullptr == _numIntegrand || !_numIntegrand->isValid()) {
686 coutE(Integration) << ClassName() << "::" << GetName() << ": failed to create valid integrand." << std::endl;
687 return false;
688 }
689
690 // Create appropriate numeric integrator using factory
692 std::string integratorName = RooNumIntFactory::instance().getIntegratorName(*_numIntegrand,*_iconfig,0,isBinned);
694
695 if(_numIntEngine == nullptr || !_numIntEngine->isValid()) {
696 coutE(Integration) << ClassName() << "::" << GetName() << ": failed to create valid integrator." << std::endl;
697 return false;
698 }
699
700 cxcoutI(NumericIntegration) << "RooRealIntegral::init(" << GetName() << ") using numeric integrator "
701 << integratorName << " to calculate Int" << _intList << std::endl ;
702
703 if (_intList.size()>3) {
704 cxcoutI(NumericIntegration) << "RooRealIntegral::init(" << GetName() << ") evaluation requires " << _intList.size() << "-D numeric integration step. Evaluation may be slow, sufficient numeric precision for fitting & minimization is not guaranteed" << std::endl ;
705 }
706
708 return true;
709}
710
711////////////////////////////////////////////////////////////////////////////////
712/// Copy constructor
713
716 _valid(other._valid),
717 _respectCompSelect(other._respectCompSelect),
718 _sumList("!sumList", this, other._sumList),
719 _intList("!intList", this, other._intList),
720 _anaList("!anaList", this, other._anaList),
721 _jacList("!jacList", this, other._jacList),
722 _facList("!facList", this, other._facList),
723 _function("!func", this, other._function),
724 _iconfig(other._iconfig),
725 _sumCat("!sumCat", this, other._sumCat),
726 _mode(other._mode),
727 _intOperMode(other._intOperMode),
728 _rangeName(other._rangeName)
729{
730 if(other._funcNormSet) {
731 _funcNormSet = std::make_unique<RooArgSet>();
732 other._funcNormSet->snapshot(*_funcNormSet, false);
733 }
734
735 other._intList.snapshot(_saveInt) ;
736 other._sumList.snapshot(_saveSum) ;
737
738}
739
740////////////////////////////////////////////////////////////////////////////////
741
745
746////////////////////////////////////////////////////////////////////////////////
747
749{
750 // Handle special case of no integration with default algorithm
751 if (iset.empty()) {
752 return RooAbsReal::createIntegral(iset,nset,cfg,rangeName) ;
753 }
754
755 // Special handling of integral of integral, return RooRealIntegral that represents integral over all dimensions in one pass
757 isetAll.add(_sumList) ;
758 isetAll.add(_intList) ;
759 isetAll.add(_anaList) ;
760 isetAll.add(_facList) ;
761
762 const RooArgSet* newNormSet(nullptr) ;
763 std::unique_ptr<RooArgSet> tmp;
764 if (nset && !_funcNormSet) {
765 newNormSet = nset ;
766 } else if (!nset && _funcNormSet) {
767 newNormSet = _funcNormSet.get();
768 } else if (nset && _funcNormSet) {
769 tmp = std::make_unique<RooArgSet>();
770 tmp->add(*nset) ;
771 tmp->add(*_funcNormSet,true) ;
772 newNormSet = tmp.get();
773 }
775}
776
777////////////////////////////////////////////////////////////////////////////////
778/// Return value of object. If the cache is clean, return the
779/// cached value, otherwise recalculate on the fly and refill
780/// the cache
781
782double RooRealIntegral::getValV(const RooArgSet* nset) const
783{
784// // fast-track clean-cache processing
785// if (_operMode==AClean) {
786// return _value ;
787// }
788
789 if (nset && nset->uniqueId().value() != _lastNormSetId) {
790 const_cast<RooRealIntegral*>(this)->setProxyNormSet(nset);
791 _lastNormSetId = nset->uniqueId().value();
792 }
793
795 _value = traceEval(nset) ;
796 }
797
798 return _value ;
799}
800
801////////////////////////////////////////////////////////////////////////////////
802/// Perform the integration and return the result
803
805{
807
808 double retVal(0) ;
809 switch (_intOperMode) {
810
811 case Hybrid:
812 {
813 // try to initialize our numerical integration engine
814 if(!(_valid= initNumIntegrator())) {
815 coutE(Integration) << ClassName() << "::" << GetName()
816 << ":evaluate: cannot initialize numerical integrator" << std::endl;
817 return 0;
818 }
819
820 // Find any function dependents that are "AClean" and switch them temporarily to "Auto".
821 // We do this by compute graph traversal and RAII objects on the heap,
822 // which seems quite expensive, but is not as bad as it looks because:
823 // 1. The sub-graphs representing numerically-integrated functions
824 // are usually small
825 // 2. The numerical integration itself dominates the runtime of the
826 // evaluation.
827 // 3. The operMode is only "AClean" if we use the constant term
828 // optimization of the legacy test statistics.
829 // 4. Once the legacy test statistics are deprecated and removed,
830 // this code block can go away (TODO when that happens).
831 // Note: in the past, the "AClean" states were changed with a global
832 // setDirtyInhibit(true) before evaluating the numeric integral. While
833 // this avoids the bookkeeping overhead, it actually changes the oper
834 // mode of all nodes to "ADirty" and not to "Auto", resulting in
835 // significant performance loss in case the target function benefits
836 // from caching subgraph results (e.g. for nested numeric integrals).
838 _function->treeNodeServerList(&serverList, nullptr, true, true, false, true);
840
841 for (auto *arg : serverList) {
842 arg->syncCache();
843 if (arg->operMode() == RooAbsArg::AClean) {
844 operModeRAII.change(arg, RooAbsArg::Auto);
845 }
846 }
847
848 // Save current integral dependent values
851
852 // Evaluate sum/integral
853 retVal = sum() ;
854
855 // This must happen BEFORE restoring dependents, otherwise no dirty state propagation in restore step
856 operModeRAII.clear();
857
858 // Restore integral dependent values
861 break ;
862 }
863 case Analytic:
864 {
866 cxcoutD(Tracing) << "RooRealIntegral::evaluate_analytic(" << GetName()
867 << ")func = " << _function->ClassName() << "::" << _function->GetName()
868 << " raw = " << retVal << " _funcNormSet = " << (_funcNormSet?*_funcNormSet:RooArgSet()) << std::endl ;
869
870
871 break ;
872 }
873
874 case PassThrough:
875 {
876 // In pass through mode, the RooRealIntegral should have registered the
877 // function as a value server, because we directly depend on its value.
879 // There should be no other servers besides the actual function and the
880 // factorized observables that the function doesn't depend on but are
881 // integrated over later.
882 assert(servers().size() == _facList.size() + 1);
883
885 break ;
886 }
887 }
888
889
890 // Multiply answer with integration ranges of factorized variables
891 for (const auto arg : _facList) {
892 // Multiply by fit range for 'real' dependents
893 if (auto argLV = dynamic_cast<RooAbsRealLValue *>(arg)) {
894 retVal *= (argLV->getMax(intRange()) - argLV->getMin(intRange())) ;
895 }
896 // Multiply by number of states for category dependents
897 if (auto argLV = dynamic_cast<RooAbsCategoryLValue *>(arg)) {
898 retVal *= argLV->numTypes() ;
899 }
900 }
901
902
903 if (dologD(Tracing)) {
904 cxcoutD(Tracing) << "RooRealIntegral::evaluate(" << GetName() << ") anaInt = " << _anaList << " numInt = " << _intList << _sumList << " mode = " ;
905 switch(_intOperMode) {
906 case Hybrid: ccoutD(Tracing) << "Hybrid" ; break ;
907 case Analytic: ccoutD(Tracing) << "Analytic" ; break ;
908 case PassThrough: ccoutD(Tracing) << "PassThrough" ; break ;
909 }
910
911 ccxcoutD(Tracing) << "raw*fact = " << retVal << std::endl ;
912 }
913
914 return retVal ;
915}
916
917////////////////////////////////////////////////////////////////////////////////
918/// Return product of jacobian terms originating from analytical integration
919
921{
922 if (_jacList.empty()) {
923 return 1 ;
924 }
925
926 double jacProd(1) ;
927 for (const auto elm : _jacList) {
928 auto arg = static_cast<const RooAbsRealLValue*>(elm);
929 jacProd *= arg->jacobian() ;
930 }
931
932 // Take std::abs() here: if jacobian is negative, min and max are swapped and analytical integral
933 // will be positive, so must multiply with positive jacobian.
934 return std::abs(jacProd) ;
935}
936
937////////////////////////////////////////////////////////////////////////////////
938/// Perform summation of list of category dependents to be integrated
939
941{
942 if (!_sumList.empty()) {
943 // Add integrals for all permutations of categories summed over
944 double total(0) ;
945
947 for (const auto& nameIdx : *sumCat) {
948 sumCat->setIndex(nameIdx);
949 if (!_rangeName || sumCat->inRange(RooNameReg::str(_rangeName))) {
951 }
952 }
953
954 return total ;
955
956 } else {
957 // Simply return integral
958 double ret = integrate() / jacobianProduct() ;
959 return ret ;
960 }
961}
962
963////////////////////////////////////////////////////////////////////////////////
964/// Perform hybrid numerical/analytical integration over all real-valued dependents
965
967{
968 if (!_numIntEngine) {
969 // Trivial case, fully analytical integration
971 } else {
972 return _numIntEngine->calculate() ;
973 }
974}
975
976////////////////////////////////////////////////////////////////////////////////
977/// Intercept server redirects and reconfigure internal object accordingly
978
980 bool mustReplaceAll, bool nameChange, bool isRecursive)
981{
983
985
986 // Update contents value caches for _intList and _sumList
991
992 // Delete parameters cache if we have one
993 _params.reset();
994
996}
997
998////////////////////////////////////////////////////////////////////////////////
999
1001{
1002 if (!_params) {
1003 _params = std::make_unique<RooArgSet>("params") ;
1004
1005 RooArgSet params ;
1006 for (const auto server : _serverList) {
1007 if (server->isValueServer(*this)) _params->add(*server) ;
1008 }
1009 }
1010
1011 return *_params ;
1012}
1013
1014////////////////////////////////////////////////////////////////////////////////
1015/// Check if current value is valid
1016
1017bool RooRealIntegral::isValidReal(double /*value*/, bool /*printError*/) const
1018{
1019 return true ;
1020}
1021
1022////////////////////////////////////////////////////////////////////////////////
1023/// Check if component selection is allowed
1024
1028
1029////////////////////////////////////////////////////////////////////////////////
1030/// Set component selection to be allowed/forbidden
1031
1035
1036////////////////////////////////////////////////////////////////////////////////
1037/// Customized printing of arguments of a RooRealIntegral to more intuitively reflect the contents of the
1038/// integration operation
1039
1040void RooRealIntegral::printMetaArgs(std::ostream& os) const
1041{
1042 if (!intVars().empty()) {
1043 os << "Int " ;
1044 }
1045 os << _function->GetName() ;
1046 if (_funcNormSet) {
1047 os << "_Norm" << *_funcNormSet << " " ;
1048 }
1049
1050 // List internally integrated observables and factorizing observables as analytically integrated
1052 tmp.add(_facList) ;
1053 if (!tmp.empty()) {
1054 os << "d[Ana]" << tmp << " ";
1055 }
1056
1057 // List numerically integrated and summed observables as numerically integrated
1059 tmp2.add(_sumList) ;
1060 if (!tmp2.empty()) {
1061 os << " d[Num]" << tmp2 << " ";
1062 }
1063}
1064
1065////////////////////////////////////////////////////////////////////////////////
1066/// Print the state of this object to the specified output stream.
1067
1068void RooRealIntegral::printMultiline(std::ostream& os, Int_t contents, bool verbose, TString indent) const
1069{
1070 RooAbsReal::printMultiline(os,contents,verbose,indent) ;
1071 os << indent << "--- RooRealIntegral ---" << std::endl;
1072 os << indent << " Integrates ";
1075 deeper.Append(" ");
1076 os << indent << " operating mode is "
1077 << (_intOperMode==Hybrid?"Hybrid":(_intOperMode==Analytic?"Analytic":"PassThrough")) << std::endl ;
1078 os << indent << " Summed discrete args are " << _sumList << std::endl ;
1079 os << indent << " Numerically integrated args are " << _intList << std::endl;
1080 os << indent << " Analytically integrated args using mode " << _mode << " are " << _anaList << std::endl ;
1081 os << indent << " Arguments included in Jacobian are " << _jacList << std::endl ;
1082 os << indent << " Factorized arguments are " << _facList << std::endl ;
1083 os << indent << " Function normalization set " ;
1084 if (_funcNormSet) {
1085 _funcNormSet->Print("1") ;
1086 } else {
1087 os << "<none>";
1088 }
1089
1090 os << std::endl ;
1091}
1092
1093////////////////////////////////////////////////////////////////////////////////
1094/// Global switch to cache all integral values that integrate at least ndim dimensions numerically
1095
1097{
1098 _cacheAllNDim = ndim;
1099}
1100
1101////////////////////////////////////////////////////////////////////////////////
1102/// Return minimum dimensions of numeric integration for which values are cached.
1103
1108
1109std::unique_ptr<RooAbsArg>
1114
1115/// Sort numeric integration variables in summation and integration lists.
1116/// To be used during construction.
1118{
1119 if (dynamic_cast<RooAbsRealLValue const *>(&arg)) {
1120 _intList.add(arg, true);
1121 } else if (dynamic_cast<RooAbsCategoryLValue const *>(&arg)) {
1122 _sumList.add(arg, true);
1123 }
1124}
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
#define cxcoutI(a)
#define cxcoutD(a)
#define oocxcoutD(o, a)
#define dologD(a)
#define coutE(a)
#define ccxcoutD(a)
#define ccoutD(a)
#define oocxcoutI(o, a)
int Int_t
Signed integer 4 bytes (int)
Definition RtypesCore.h:60
static void indent(ostringstream &buf, int indent_level)
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
static unsigned int total
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t index
char name[80]
Definition TGX11.cxx:148
char * Form(const char *fmt,...)
Formats a string in a circular formatting buffer.
Definition TString.cxx:2570
Scope guard that temporarily changes the operation mode of one or more RooAbsArg instances.
const_iterator begin() const
const_iterator end() const
Common abstract base class for objects that represent a value and a "shape" in RooFit.
Definition RooAbsArg.h:76
RooExpensiveObjectCache & expensiveObjectCache() const
bool dependsOn(const RooAbsCollection &serverList, const RooAbsArg *ignoreArg=nullptr, bool valueOnly=false) const
Test whether we depend on (ie, are served by) any object in the specified collection.
void setOperMode(OperMode mode, bool recurseADirty=true)
Set the operation mode of this node.
RooFit::OwningPtr< RooArgSet > getObservables(const RooArgSet &set, bool valueOnly=true) const
Given a set of possible observables, return the observables that this PDF depends on.
virtual void setExpensiveObjectCache(RooExpensiveObjectCache &cache)
Definition RooAbsArg.h:439
bool addOwnedComponents(const RooAbsCollection &comps)
Take ownership of the contents of 'comps'.
virtual std::unique_ptr< RooAbsArg > compileForNormSet(RooArgSet const &normSet, RooFit::Detail::CompileContext &ctx) const
const RefCountList_t & servers() const
List of all servers of this object.
Definition RooAbsArg.h:145
void addServer(RooAbsArg &server, bool valueProp=true, bool shapeProp=false, std::size_t refCount=1)
Register another RooAbsArg as a server to us, ie, declare that we depend on it.
virtual bool isDerived() const
Does value or shape of this arg depend on any other arg?
Definition RooAbsArg.h:97
bool isValueOrShapeDirtyAndClear() const
Definition RooAbsArg.h:390
void setProxyNormSet(const RooArgSet *nset)
Forward a change in the cached normalization argset to all the registered proxies.
void branchNodeServerList(RooAbsCollection *list, const RooAbsArg *arg=nullptr, bool recurseNonDerived=false) const
Fill supplied list with all branch nodes of the arg tree starting with ourself as top node.
RefCountList_t _serverList
Definition RooAbsArg.h:565
void treeNodeServerList(RooAbsCollection *list, const RooAbsArg *arg=nullptr, bool doBranch=true, bool doLeaf=true, bool valueOnly=false, bool recurseNonDerived=false) const
Fill supplied list with nodes of the arg tree, following all server links, starting with ourself as t...
Abstract base class for objects that represent a discrete value that can be set from the outside,...
Abstract container object that can hold multiple RooAbsArg objects.
RooFit::UniqueId< RooAbsCollection > const & uniqueId() const
Returns a unique ID that is different for every instantiated RooAbsCollection.
virtual void removeAll()
Remove all arguments from our set, deleting them if we own them.
Int_t index(const RooAbsArg *arg) const
Returns index of given arg, or -1 if arg is not in the collection.
void assign(const RooAbsCollection &other) const
Sets the value, cache and constant attribute of any argument in our set that also appears in the othe...
Storage_t::size_type size() const
RooAbsArg * first() const
RooAbsArg * find(const char *name) const
Find object with given name in list.
Abstract base class for objects that are lvalues, i.e.
Abstract base class for objects that represent a real value that may appear on the left hand side of ...
Abstract base class for objects that represent a real value and implements functionality common to al...
Definition RooAbsReal.h:63
double getVal(const RooArgSet *normalisationSet=nullptr) const
Evaluate object.
Definition RooAbsReal.h:107
virtual Int_t getAnalyticalIntegralWN(RooArgSet &allVars, RooArgSet &analVars, const RooArgSet *normSet, const char *rangeName=nullptr) const
Variant of getAnalyticalIntegral that is also passed the normalization set that should be applied to ...
void printMultiline(std::ostream &os, Int_t contents, bool verbose=false, TString indent="") const override
Structure printing.
bool redirectServersHook(const RooAbsCollection &newServerList, bool mustReplaceAll, bool nameChange, bool isRecursiveStep) override
Function that is called at the end of redirectServers().
virtual bool forceAnalyticalInt(const RooAbsArg &) const
Definition RooAbsReal.h:170
double _value
Cache for current value of object.
Definition RooAbsReal.h:542
double traceEval(const RooArgSet *set) const
Calculate current value of object, with error tracing wrapper.
RooFit::UniqueId< RooArgSet >::Value_t _lastNormSetId
!
Definition RooAbsReal.h:549
virtual double analyticalIntegralWN(Int_t code, const RooArgSet *normSet, const char *rangeName=nullptr) const
Implements the actual analytical integral(s) advertised by getAnalyticalIntegral.
const RooNumIntConfig * getIntegratorConfig() const
Return the numeric integration configuration used for this object.
virtual bool isBinnedDistribution(const RooArgSet &) const
Tests if the distribution is binned. Unless overridden by derived classes, this always returns false.
Definition RooAbsReal.h:343
RooFit::OwningPtr< RooAbsReal > createIntegral(const RooArgSet &iset, const RooCmdArg &arg1, const RooCmdArg &arg2={}, const RooCmdArg &arg3={}, const RooCmdArg &arg4={}, const RooCmdArg &arg5={}, const RooCmdArg &arg6={}, const RooCmdArg &arg7={}, const RooCmdArg &arg8={}) const
Create an object that represents the integral of the function over one or more observables listed in ...
RooArgList is a container object that can hold multiple RooAbsArg objects.
Definition RooArgList.h:22
bool _valueServer
If true contents is value server of owner.
Definition RooArgProxy.h:80
bool isValueServer() const
Returns true of contents is value server of owner.
Definition RooArgProxy.h:60
RooArgSet is a container object that can hold multiple RooAbsArg objects.
Definition RooArgSet.h:24
RooArgSet * snapshot(bool deepCopy=true) const
Use RooAbsCollection::snapshot(), but return as RooArgSet.
Definition RooArgSet.h:159
void removeAll() override
Remove all argument inset using remove(const RooAbsArg&).
bool addOwned(RooAbsArg &var, bool silent=false) override
Overloaded RooCollection_t::addOwned() method insert object into owning set and registers object as s...
bool add(const RooAbsArg &var, bool valueServer, bool shapeServer, bool silent)
Overloaded RooCollection_t::add() method insert object into set and registers object as server to own...
Represents a constant real-valued object.
Definition RooConstVar.h:23
Registry for const char* names.
Definition RooNameReg.h:26
static const char * str(const TNamed *ptr)
Return C++ string corresponding to given TNamed pointer.
Definition RooNameReg.h:39
Holds the configuration parameters of the various numeric integrators used by RooRealIntegral.
static RooNumIntFactory & instance()
Static method returning reference to singleton instance of factory.
virtual void printStream(std::ostream &os, Int_t contents, StyleOption style, TString indent="") const
Print description of object on ostream, printing contents set by contents integer,...
Performs hybrid numerical/analytical integrals of RooAbsReal objects.
RooNumIntConfig * _iconfig
bool initNumIntegrator() const
(Re)Initialize numerical integration engine if necessary.
RooArgSet const * funcNormSet() const
RooFit::OwningPtr< RooAbsReal > createIntegral(const RooArgSet &iset, const RooArgSet *nset=nullptr, const RooNumIntConfig *cfg=nullptr, const char *rangeName=nullptr) const override
Create an object that represents the integral of the function over one or more observables listed in ...
void setAllowComponentSelection(bool allow)
Set component selection to be allowed/forbidden.
RooRealProxy _function
Function being integrated.
RooArgSet intVars() const
RooSetProxy _intList
Set of continuous observables over which is integrated numerically.
virtual double sum() const
Perform summation of list of category dependents to be integrated.
RooSetProxy _facList
Set of observables on which function does not depends, which are integrated nevertheless.
std::unique_ptr< RooArgSet > _params
! cache for set of parameters
static void setCacheAllNumeric(Int_t ndim)
Global switch to cache all integral values that integrate at least ndim dimensions numerically.
IntOperMode _intOperMode
integration operation mode
double evaluate() const override
Perform the integration and return the result.
const RooArgSet & parameters() const
std::unique_ptr< RooAbsFunc > _numIntegrand
!
void addNumIntDep(RooAbsArg const &arg)
Sort numeric integration variables in summation and integration lists.
RooSetProxy _jacList
Set of lvalue observables over which is analytically integration that have a non-unit Jacobian.
bool isValidReal(double value, bool printError=false) const override
Check if current value is valid.
double getValV(const RooArgSet *set=nullptr) const override
Return value of object.
RooSetProxy _anaList
Set of observables over which is integrated/summed analytically.
bool redirectServersHook(const RooAbsCollection &newServerList, bool mustReplaceAll, bool nameChange, bool isRecursive) override
Intercept server redirects and reconfigure internal object accordingly.
RooSetProxy _sumList
Set of discrete observable over which is summed numerically.
~RooRealIntegral() override
void printMetaArgs(std::ostream &os) const override
Customized printing of arguments of a RooRealIntegral to more intuitively reflect the contents of the...
void printMultiline(std::ostream &os, Int_t contents, bool verbose=false, TString indent="") const override
Print the state of this object to the specified output stream.
std::unique_ptr< RooAbsIntegrator > _numIntEngine
!
virtual double integrate() const
Perform hybrid numerical/analytical integration over all real-valued dependents.
RooListProxy _sumCat
!
virtual double jacobianProduct() const
Return product of jacobian terms originating from analytical integration.
static Int_t getCacheAllNumeric()
Return minimum dimensions of numeric integration for which values are cached.
static Int_t _cacheAllNDim
! Cache all integrals with given numeric dimension
RooArgSet const * actualFuncNormSet() const
std::unique_ptr< RooArgSet > _funcNormSet
Optional normalization set passed to function.
std::unique_ptr< RooAbsArg > compileForNormSet(RooArgSet const &normSet, RooFit::Detail::CompileContext &ctx) const override
void autoSelectDirtyMode()
Set appropriate cache operation mode for integral depending on cache operation mode of server objects...
const char * intRange() const
bool getAllowComponentSelection() const
Check if component selection is allowed.
Joins several RooAbsCategoryLValue objects into a single category.
bool setArg(T &newRef)
Change object held in proxy into newRef.
The TNamed class is the base class for all named ROOT classes.
Definition TNamed.h:29
const char * GetName() const override
Returns name of object.
Definition TNamed.h:49
virtual const char * ClassName() const
Returns name of class to which the object belongs.
Definition TObject.cxx:226
Basic string class.
Definition TString.h:138
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
constexpr Value_t value() const
Return numerical value of ID.
Definition UniqueId.h:59