Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
RooSimWSTool.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 /// \class RooSimWSTool
19 /// The RooSimWSTool is a tool operating on RooWorkspace objects that
20 /// can clone PDFs into a series of variations that are joined together
21 /// into a RooSimultanous PDF.
22 ///
23 /// ## Splitting a single PDF
24 /// The simplest use case is to take a workspace PDF as prototype and
25 /// "split" a parameter of that PDF into two specialized parameters
26 /// depending on a category in the dataset.
27 ///
28 /// For example, given a Gaussian
29 /// PDF \f$ G(x \,|\, m,s) \f$ we want to construct a \f$ G_a(x \,|\, m_a,s) \f$ and a \f$ G_b(x \,|\, m_b,s) \f$
30 /// with different mean parameters to be fit to a dataset with observables
31 /// \f$ (x,c) \f$ where \f$ c \f$ is a category with states 'a' and 'b'.
32 ///
33 /// Using RooSimWSTool, one can create a simultaneous PDF from \f$ G_a \f$ and \f$ G_b \f$
34 /// from \f$ G \f$ with the following commands:
35 /// ```
36 /// RooSimWSTool wst(wspace);
37 /// wst.build("G_sim", "G", SplitParam("m","c"));
38 /// ```
39 ///
40 /// #### Splitting using a product category
41 /// From this simple example one can go to builds of arbitrary complexity
42 /// by specifying multiple SplitParam arguments on multiple parameters
43 /// involving multiple splitting categories. Splits can also be performed
44 /// in the product of multiple categories, *i.e.*,
45 /// ```
46 /// wst.build("G_sim", "G", SplitParam("m","c,d"));
47 /// ```
48 /// splits the parameter \f$ m \f$ in the product of the states of \f$ c \f$ and
49 /// \f$ d \f$.
50 ///
51 /// #### Constrained split
52 /// Another possibility
53 /// is the "constrained" split, which clones the parameter for all but one state
54 /// and inserts a formula specialization in a chosen state that evaluates
55 /// to \f$ 1 - \sum_i(a_i) \f$ where \f$ a_i \f$ are all other specializations. For example,
56 /// given a category \f$ c \f$ with the states `"A","B","C","D"`, the specification
57 /// ```
58 /// SplitParamConstrained("m","c","D")
59 /// ```
60 /// will create the parameters \f$ m_A,m_B,m_C \f$ and a formula expression \f$ m_D \f$
61 /// that evaluates to \f$ (1-(m_A+m_B+m_C)) \f$. Constrained splits can also be
62 /// specified in the product of categories. In that case, the name of the
63 /// remainder state follows the syntax `"{State1;State2}"`, where `State1` and
64 /// `State2` are the state names of the two spitting categories.
65 ///
66 /// ## Splitting multiple PDFs
67 /// The examples so far deal with a single prototype PDF. It is also
68 /// possible to build with multiple prototype PDFs by specifying a
69 /// mapping between the prototype to use and the names of states of
70 /// a "master" splitting category. To specify these configurations,
71 /// an intermediate `MultiBuildConfig` must be composed with all
72 /// the necessary specifications. This, for example,
73 /// ```
74 /// RooSimWSTool::MultiBuildConfig mbc("mc");
75 /// mbc.addPdf("I","G",SplitParam("m,s","c"));
76 /// mbc.addPdf("II,III","F",SplitParam("a","c,d"));
77 /// ```
78 /// configures a build with two prototype PDFs \f$ G \f$ and \f$ F \f$.
79 /// Prototype \f$ G \f$ is used for state `"I"` of the master split category
80 /// `mc` and prototype \f$ F \f$ is used for states `"II"` and `"III"` of the
81 /// master split category `mc`. Furthermore, the parameters \f$ m,s \f$ of
82 /// prototype \f$ G \f$ are split
83 /// in category \f$ c \f$ while the parameter \f$ a \f$ of prototype \f$ F \f$ is split in
84 /// the product of the categories \f$ c \f$ and \f$ d \f$. The actual build is then
85 /// performed by passing the build configuration to RooSimWSTool, *e.g.*,
86 /// ```
87 /// wst.build("MASTER", mbc);
88 /// ```
89 /// By default, a specialisation is built for each permutation of
90 /// states of the splitting categories that are used. It is possible
91 /// to restrict the building of specialised PDFs to a subset of states
92 /// by adding a restriction on the number of states to build as follows:
93 /// ```
94 /// mbc.restrictBuild("c","A,B");
95 /// ```
96 /// The restrictBuild method can be called multiple times, but at most
97 /// once for each splitting category in use. For simple builds with a single
98 /// prototype, restriction can be specified with a Restrict() argument
99 /// on the build command line.
100 ///
101 
102 
103 #include "RooFit.h"
104 #include "RooSimWSTool.h"
105 #include "RooMsgService.h"
106 #include "RooCategory.h"
107 #include "RooRealVar.h"
108 #include "RooAbsPdf.h"
109 #include "RooStringVar.h"
110 #include "RooSuperCategory.h"
111 #include "RooCatType.h"
112 #include "RooCustomizer.h"
113 #include "RooMultiCategory.h"
114 #include "RooSimultaneous.h"
115 #include "RooGlobalFunc.h"
116 #include "RooFracRemainder.h"
117 #include "RooFactoryWSTool.h"
118 
119 ClassImp(RooSimWSTool);
120 ClassImp(RooSimWSTool::BuildConfig);
121 ClassImp(RooSimWSTool::MultiBuildConfig);
122 ClassImp(RooSimWSTool::SplitRule);
123 ClassImp(RooSimWSTool::ObjBuildConfig);
124 ClassImp(RooSimWSTool::ObjSplitRule);
125 ;
126 
127 using namespace std ;
128 
129 
130 static Int_t init();
131 
132 static Int_t dummy = init() ;
133 
134 static Int_t init()
135 {
136  RooFactoryWSTool::IFace* iface = new RooSimWSTool::SimWSIFace ;
137  RooFactoryWSTool::registerSpecial("SIMCLONE",iface) ;
138  RooFactoryWSTool::registerSpecial("MSIMCLONE",iface) ;
139  (void) dummy;
140  return 0 ;
141 }
142 
143 
144 ////////////////////////////////////////////////////////////////////////////////
145 /// Constructor of SimWSTool on given workspace. All input is taken from the workspace
146 /// All output is stored in the workspace
147 
148 RooSimWSTool::RooSimWSTool(RooWorkspace& ws) : _ws(&ws)
149 {
150 }
151 
152 
153 
154 ////////////////////////////////////////////////////////////////////////////////
155 /// Destructor
156 
157 RooSimWSTool::~RooSimWSTool()
158 {
159 }
160 
161 
162 
163 ////////////////////////////////////////////////////////////////////////////////
164 /// Build a RooSimultaneous PDF with name simPdfName from cloning specializations of protytpe PDF protoPdfName.
165 /// <table>
166 /// <tr><th> Optional Arguments <th> Effect
167 /// <tr><td> SplitParam(varname, catname) <td> Split parameter(s) with given name(s) in category(s) with given names
168 /// <tr><td> SplitParam(var, cat) <td> Split given parameter(s) in givem category(s)
169 /// <tr><td> SplitParamConstrained(vname, cname, remainder) <td> Make constrained split in parameter(s) with given name(s) in category(s) with given names
170 /// putting remainder fraction formula in state with name "remainder"
171 /// <tr><td> SplitParamConstrained(var,cat,remainder) <td> Make constrained split in parameter(s) with given name(s) in category(s) with given names
172 /// putting remainder fraction formula in state with name "remainder"
173 /// <tr><td> Restrict(catName,stateNameList) <td> Restrict build by only considered listed state names of category with given name
174 
175 RooSimultaneous* RooSimWSTool::build(const char* simPdfName, const char* protoPdfName, const RooCmdArg& arg1,const RooCmdArg& arg2,
176  const RooCmdArg& arg3,const RooCmdArg& arg4, const RooCmdArg& arg5,const RooCmdArg& arg6)
177 {
178  BuildConfig bc(protoPdfName,arg1,arg2,arg3,arg4,arg5,arg6) ;
179  return build(simPdfName,bc) ;
180 }
181 
182 
183 
184 ////////////////////////////////////////////////////////////////////////////////
185 /// Build a RooSimultaneous PDF with name simPdfName from cloning specializations of protytpe PDF protoPdfName.
186 /// Use the provided BuildConfig or MultiBuildConfig object to configure the build
187 
188 RooSimultaneous* RooSimWSTool::build(const char* simPdfName,BuildConfig& bc, Bool_t verbose)
189 {
190  ObjBuildConfig* obc = validateConfig(bc) ;
191  if (!obc) return 0 ;
192 
193  if (verbose) {
194  obc->print() ;
195  }
196 
197  RooSimultaneous* ret = executeBuild(simPdfName,*obc,verbose) ;
198 
199  delete obc ;
200  return ret ;
201 }
202 
203 
204 
205 ////////////////////////////////////////////////////////////////////////////////
206 /// Validate build configuration. If not syntax errors or missing objects are found,
207 /// return an ObjBuildConfig in which all names are replaced with object pointers.
208 
209 RooSimWSTool::ObjBuildConfig* RooSimWSTool::validateConfig(BuildConfig& bc)
210 {
211  // Create empty object version of build config
212  ObjBuildConfig* obc = new ObjBuildConfig ;
213 
214  if (bc._masterCatName.length()>0) {
215  obc->_masterCat = _ws->cat(bc._masterCatName.c_str()) ;
216  if (!obc->_masterCat) {
217  coutE(ObjectHandling) << "RooSimWSTool::build(" << GetName() << ") ERROR: associated workspace " << _ws->GetName()
218  << " does not contain a category named " << bc._masterCatName
219  << " that was designated as master index category in the build configuration" << endl ;
220  delete obc ;
221  return 0 ;
222  }
223  } else {
224  obc->_masterCat = 0 ;
225  }
226 
227  map<string,SplitRule>::iterator pdfiter ;
228  // Check that we have the p.d.f.s
229  for (pdfiter = bc._pdfmap.begin() ; pdfiter != bc._pdfmap.end() ; ++pdfiter) {
230 
231  // Check that p.d.f exists
232  RooAbsPdf* pdf = _ws->pdf(pdfiter->second.GetName()) ;
233  if (!pdf) {
234  coutE(ObjectHandling) << "RooSimWSTool::build(" << GetName() << ") ERROR: associated workspace " << _ws->GetName()
235  << " does not contain a pdf named " << pdfiter->second.GetName() << endl ;
236  delete obc ;
237  return 0 ;
238  }
239 
240  // Create empty object version of split rule set
241  ObjSplitRule osr ;
242 
243  // Convert names of parameters and splitting categories to objects in workspace, fill object split rule
244  SplitRule& sr = pdfiter->second ;
245 
246  map<string, pair<list<string>,string> >::iterator pariter ;
247  for (pariter=sr._paramSplitMap.begin() ; pariter!=sr._paramSplitMap.end() ; ++pariter) {
248 
249  // Check that variable with given name exists in workspace
250  RooAbsArg* farg = _ws->fundArg(pariter->first.c_str()) ;
251  if (!farg) {
252  coutE(ObjectHandling) << "RooSimWSTool::build(" << GetName() << ") ERROR: associated workspace " << _ws->GetName()
253  << " does not contain a variable named " << pariter->first.c_str()
254  << " as specified in splitting rule of parameter " << pariter->first << " of p.d.f " << pdf << endl ;
255  delete obc ;
256  return 0 ;
257  }
258 
259  // Check that given variable is indeed related to given p.d.f
260  if (!pdf->dependsOn(*farg)) {
261  coutE(ObjectHandling) << "RooSimWSTool::build(" << GetName() << ") ERROR: specified parameter " << pariter->first
262  << " in split is not function of p.d.f " << pdf->GetName() << endl ;
263  delete obc ;
264  return 0 ;
265  }
266 
267 
268  RooArgSet splitCatSet ;
269  list<string>::iterator catiter ;
270  for (catiter = pariter->second.first.begin() ; catiter!=pariter->second.first.end() ; ++catiter) {
271  RooAbsCategory* cat = _ws->catfunc(catiter->c_str()) ;
272  if (!cat) {
273  coutE(ObjectHandling) << "RooSimWSTool::build(" << GetName() << ") ERROR: associated workspace " << _ws->GetName()
274  << " does not contain a category named " << catiter->c_str()
275  << " as specified in splitting rule of parameter " << pariter->first << " of p.d.f " << pdf << endl ;
276  delete obc ;
277  return 0 ;
278  }
279  splitCatSet.add(*cat) ;
280  }
281 
282  // Check if composite splitCatSet does not contain category functions that depend on other categories used in the same split
283  TIterator* iter = splitCatSet.createIterator() ;
284  RooAbsArg* arg ;
285  while((arg=(RooAbsArg*)iter->Next())) {
286  RooArgSet tmp(splitCatSet) ;
287  tmp.remove(*arg) ;
288  if (arg->dependsOnValue(tmp)) {
289  coutE(InputArguments) << "RooSimWSTool::build(" << GetName() << ") ERROR: Ill defined split: splitting category function " << arg->GetName()
290  << " used in composite split " << splitCatSet << " of parameter " << farg->GetName() << " of pdf " << pdf->GetName()
291  << " depends on one or more of the other splitting categories in the composite split" << endl ;
292  delete obc ;
293  delete iter ;
294  return 0 ;
295  }
296  }
297  delete iter ;
298 
299  // If a constrained split is specified, check that split parameter is a real-valued type
300  if (pariter->second.second.size()>0) {
301  if (!dynamic_cast<RooAbsReal*>(farg)) {
302  coutE(InputArguments) << "RooSimWSTool::build(" << GetName() << ") ERROR: Constrained split specified in non real-valued parameter " << farg->GetName() << endl ;
303  delete obc ;
304  return 0 ;
305  }
306  }
307 
308  // Fill object build config with object split rule
309  osr._paramSplitMap[farg].first.add(splitCatSet) ;
310  osr._paramSplitMap[farg].second = pariter->second.second ;
311 
312  // For multi-pdf configurations, check that the master index state name associated with this p.d.f exists as a state in the master category
313  if (obc->_masterCat) {
314  list<string>::iterator misi ;
315  for (misi=sr._miStateNameList.begin() ; misi!=sr._miStateNameList.end() ; ++misi) {
316  const RooCatType* ctype = obc->_masterCat->lookupType(misi->c_str(),kFALSE) ;
317  if (ctype==0) {
318  coutE(ObjectHandling) << "RooSimWSTool::build(" << GetName() << ") ERROR: master index category " << obc->_masterCat->GetName()
319  << " does not have a state named " << *misi << " which was specified as state associated with p.d.f "
320  << sr.GetName() << endl ;
321  delete obc ;
322  return 0 ;
323  }
324  osr._miStateList.push_back(ctype) ;
325  }
326  }
327 
328  // Add specified split cats to global list of all splitting categories
329  obc->_usedSplitCats.add(splitCatSet,kTRUE) ;
330 
331  }
332  // Need to add clause here for SplitRules without any split (which can happen in MultiBuildConfigs)
333  if (sr._paramSplitMap.size()==0) {
334 
335  if (obc->_masterCat) {
336  list<string>::iterator misi ;
337  for (misi=sr._miStateNameList.begin() ; misi!=sr._miStateNameList.end() ; ++misi) {
338  const RooCatType* ctype = obc->_masterCat->lookupType(misi->c_str(),kFALSE) ;
339  if (ctype==0) {
340  coutE(ObjectHandling) << "RooSimWSTool::build(" << GetName() << ") ERROR: master index category " << obc->_masterCat->GetName()
341  << " does not have a state named " << *misi << " which was specified as state associated with p.d.f "
342  << sr.GetName() << endl ;
343  delete obc ;
344  return 0 ;
345  }
346  osr._miStateList.push_back(ctype) ;
347  }
348  }
349  }
350 
351  obc->_pdfmap[pdf] = osr ;
352 
353  }
354 
355  // Check validity of build restriction specifications, if any
356  map<string,string>::iterator riter ;
357  for (riter=bc._restr.begin() ; riter!=bc._restr.end() ; ++riter) {
358  RooCategory* cat = _ws->cat(riter->first.c_str()) ;
359  if (!cat) {
360  coutE(ObjectHandling) << "RooSimWSTool::build(" << GetName() << ") ERROR: associated workspace " << _ws->GetName()
361  << " does not contain a category named " << riter->first
362  << " for which build was requested to be restricted to states " << riter->second << endl ;
363  delete obc ;
364  return 0 ;
365  }
366 
367  char buf[4096] ;
368  list<const RooCatType*> rlist ;
369  strlcpy(buf,riter->second.c_str(),4096) ;
370 
371  char* tok = strtok(buf,"{,}") ;
372  while(tok) {
373  const RooCatType* ctype = cat->lookupType(tok,kFALSE) ;
374  if (!ctype) {
375  coutE(ObjectHandling) << "RooSimWSTool::build(" << GetName() << ") ERROR: restricted build category " << cat->GetName()
376  << " does not have state " << tok << " as specified in restriction list" << endl ;
377  delete obc ;
378  return 0 ;
379  }
380  rlist.push_back(ctype) ;
381  tok = strtok(0,"{,}") ;
382  }
383 
384  obc->_restr[cat] = rlist ;
385  }
386 
387  return obc ;
388 }
389 
390 
391 
392 
393 ////////////////////////////////////////////////////////////////////////////////
394 /// Internal build driver from validation ObjBuildConfig.
395 
396 RooSimultaneous* RooSimWSTool::executeBuild(const char* simPdfName, ObjBuildConfig& obc, Bool_t verbose)
397 {
398  RooArgSet cleanupList ;
399 
400  RooAbsCategoryLValue* physCat = obc._masterCat ;
401 
402  RooArgSet physModelSet ;
403  map<string,RooAbsPdf*> stateMap ;
404 
405  map<RooAbsPdf*,ObjSplitRule>::iterator physIter = obc._pdfmap.begin() ;
406  while(physIter!=obc._pdfmap.end()) {
407 
408 
409  RooAbsPdf* physModel = physIter->first ;
410  physModelSet.add(*physModel,kTRUE) ; // silence duplicate insertion warnings
411 
412  list<const RooCatType*>::iterator stiter ;
413  for (stiter=physIter->second._miStateList.begin() ; stiter!=physIter->second._miStateList.end() ; ++stiter) {
414  stateMap[(*stiter)->GetName()] = physModel ;
415  }
416 
417  // Continue with next mapping
418  ++physIter ;
419  }
420  if (verbose) coutI(ObjectHandling) << "RooSimWSTool::executeBuild: list of prototype pdfs " << physModelSet << endl ;
421 
422  RooArgSet splitCatSet(obc._usedSplitCats) ;
423  if (physCat) splitCatSet.add(*physCat) ;
424 
425  RooArgSet splitCatSetFund ;
426  TIterator* scsiter = splitCatSet.createIterator() ;
427  RooAbsCategory* scat ;
428  while((scat=(RooAbsCategory*)scsiter->Next())) {
429  if (scat->isFundamental()) {
430  splitCatSetFund.add(*scat) ;
431  } else {
432  RooArgSet* scatvars = scat->getVariables() ;
433  splitCatSetFund.add(*scatvars) ;
434  delete scatvars ;
435  }
436  }
437  delete scsiter ;
438 
439 
440  RooAbsCategoryLValue* masterSplitCat ;
441  if (splitCatSetFund.getSize()>1) {
442  masterSplitCat = new RooSuperCategory("masterSplitCat","Master splitting category",splitCatSetFund) ;
443  } else {
444  masterSplitCat = (RooAbsCategoryLValue*) splitCatSetFund.first() ;
445  }
446  if (verbose) coutI(ObjectHandling) << "RooSimWSTool::executeBuild: list of splitting categories " << splitCatSet << endl ;
447 
448  RooArgSet splitNodeListOwned ; // owns all newly created components
449  RooArgSet splitNodeListAll ; // all leaf nodes, preload with ws contents to auto-connect existing specializations
450  TList* customizerList = new TList ;
451 
452  // Loop over requested physics models and build components
453  TIterator* physMIter = physModelSet.createIterator() ;
454  RooAbsPdf* physModel ;
455  while((physModel=(RooAbsPdf*)physMIter->Next())) {
456  if (verbose) coutI(ObjectHandling) << "RooSimPdfBuilder::executeBuild: processing prototype pdf " << physModel->GetName() << endl ;
457 
458  RooCustomizer* physCustomizer = new RooCustomizer(*physModel,*masterSplitCat,splitNodeListOwned,&splitNodeListAll) ;
459  customizerList->Add(physCustomizer) ;
460 
461  map<RooAbsArg*, pair<RooArgSet,string> >::iterator splitIter ;
462  for (splitIter = obc._pdfmap[physModel]._paramSplitMap.begin() ; splitIter != obc._pdfmap[physModel]._paramSplitMap.end() ; ++splitIter) {
463 
464  // If split is composite, first make multicategory with name 'A,B,C' and insert in WS
465 
466  // Construct name of (composite) split category (function)
467  RooArgSet& splitCatSetTmp = splitIter->second.first ;
468  string splitName = makeSplitName(splitCatSetTmp) ;
469 
470  // If composite split object does not exist yet, create it now
471  RooAbsCategory* splitCat = _ws->catfunc(splitName.c_str()) ;
472  if (!splitCat) {
473  splitCat = new RooMultiCategory(splitName.c_str(),splitName.c_str(),splitCatSetTmp) ;
474  cleanupList.addOwned(*splitCat) ;
475  _ws->import(*splitCat,RooFit::Silence(!verbose)) ;
476  }
477 
478  // If remainder category needs to be made, create RFV of appropriate for that and insert in WS
479  if(splitIter->second.second.size()>0) {
480 
481  // Check that specified split name is in fact valid
482  if (!splitCat->lookupType(splitIter->second.second.c_str())) {
483  coutE(InputArguments) << "RooSimWSTool::executeBuild(" << GetName() << ") ERROR: name of remainder state for constrained split, '"
484  << splitIter->second.second << "' , does not match any state name of (composite) split category " << splitCat->GetName() << endl ;
485  return 0 ;
486  }
487 
488  // First build manually the specializations of all non-remainder states, as the remainder state depends on these
489  RooArgSet fracLeafList ;
490  TIterator* sctiter = splitCat->typeIterator() ;
491  RooCatType* type ;
492  while((type=(RooCatType*)sctiter->Next())) {
493 
494  // Skip remainder state
495  if (splitIter->second.second == type->GetName()) continue ;
496 
497  // Construct name of split leaf
498  TString splitLeafName(splitIter->first->GetName()) ;
499  splitLeafName.Append("_") ;
500  splitLeafName.Append(type->GetName()) ;
501 
502  // Check if split leaf already exists
503  RooAbsArg* splitLeaf = _ws->fundArg(splitLeafName) ;
504  if (!splitLeaf) {
505  // If not create it now
506  splitLeaf = (RooAbsArg*) splitIter->first->clone(splitLeafName) ;
507  _ws->import(*splitLeaf,RooFit::Silence(!verbose)) ;
508  }
509  fracLeafList.add(*splitLeaf) ;
510  }
511  delete sctiter ;
512 
513 
514  // Build specialization for remainder state and insert in workspace
515  RooFracRemainder* fracRem = new RooFracRemainder(Form("%s_%s",splitIter->first->GetName(),splitIter->second.second.c_str()),"Remainder fraction",fracLeafList) ;
516  cleanupList.addOwned(*fracRem) ;
517  _ws->import(*fracRem) ;
518 
519  }
520 
521 
522  // Add split definition to customizer
523  physCustomizer->splitArgs(*splitIter->first,*splitCat) ;
524  }
525  }
526  delete physMIter ;
527 
528  // List all existing workspace components as prebuilt items for the customizers at this point
529  splitNodeListAll.add(_ws->components()) ;
530 
531  if (verbose) coutI(ObjectHandling) << "RooSimWSTool::executeBuild: configured customizers for all prototype pdfs" << endl ;
532 
533  // Create fit category from physCat and splitCatList ;
534  RooArgSet fitCatList ;
535  if (physCat) fitCatList.add(*physCat) ;
536 
537  // Add observables of splitCatSet members, rather than splitCatSet members directly
538  // as there may be cat->cat functions in here
539  scsiter = splitCatSet.createIterator() ;
540  while((scat=(RooAbsCategory*)scsiter->Next())) {
541  if (scat->isFundamental()) {
542  fitCatList.add(*scat) ;
543  } else {
544  RooArgSet* scatvars = scat->getVariables() ;
545  fitCatList.add(*scatvars) ;
546  delete scatvars ;
547  }
548  }
549  delete scsiter ;
550 
551 
552  TIterator* fclIter = fitCatList.createIterator() ;
553  string mcatname = string(simPdfName) + "_index" ;
554  RooAbsCategoryLValue* fitCat = 0 ;
555  if (fitCatList.getSize()>1) {
556  fitCat = new RooSuperCategory(mcatname.c_str(),mcatname.c_str(),fitCatList) ;
557  cleanupList.addOwned(*fitCat) ;
558  } else {
559  fitCat = (RooAbsCategoryLValue*) fitCatList.first() ;
560  }
561 
562  // Create master PDF
563  RooSimultaneous* simPdf = new RooSimultaneous(simPdfName,simPdfName,*fitCat) ;
564  cleanupList.addOwned(*simPdf) ;
565 
566  // Add component PDFs to master PDF
567  TIterator* fcIter = fitCat->typeIterator() ;
568 
569  RooCatType* fcState ;
570  while((fcState=(RooCatType*)fcIter->Next())) {
571  // Select fitCat state
572  fitCat->setLabel(fcState->GetName()) ;
573 
574  // Check if this fitCat state is selected
575  fclIter->Reset() ;
576  RooAbsCategory* splitCat ;
577  Bool_t select(kFALSE) ;
578  if (obc._restr.size()>0) {
579  while((splitCat=(RooAbsCategory*)fclIter->Next())) {
580  // Find selected state list
581 
582  list<const RooCatType*> slist = obc._restr[splitCat] ;
583  if (slist.empty()) {
584  continue ;
585  }
586 
587  list<const RooCatType*>::iterator sli ;
588  for (sli=slist.begin() ; sli!=slist.end() ; ++sli) {
589  if (string(splitCat->getLabel())==(*sli)->GetName()) {
590  select=kTRUE ;
591  }
592  }
593  }
594  if (!select) continue ;
595  } else {
596  select = kTRUE ;
597  }
598 
599  // Select appropriate PDF for this physCat state
600  RooCustomizer* physCustomizer ;
601  if (physCat) {
602  RooAbsPdf* pdf = stateMap[physCat->getLabel()] ;
603  if (pdf==0) {
604  continue ;
605  }
606  physCustomizer = (RooCustomizer*) customizerList->FindObject(pdf->GetName());
607  } else {
608  physCustomizer = (RooCustomizer*) customizerList->First() ;
609  }
610 
611  if (verbose) coutI(ObjectHandling) << "RooSimWSTool::executeBuild: Customizing prototype pdf " << physCustomizer->GetName()
612  << " for mode " << fcState->GetName() << endl ;
613 
614  // Customizer PDF for current state and add to master simPdf
615  RooAbsPdf* fcPdf = (RooAbsPdf*) physCustomizer->build(masterSplitCat->getLabel(),kFALSE) ;
616  simPdf->addPdf(*fcPdf,fcState->GetName()) ;
617  }
618  delete fcIter ;
619 
620  _ws->import(*simPdf,obc._conflProtocol,RooFit::Silence(!verbose)) ;
621 
622  // Delete customizers
623  customizerList->Delete() ;
624  delete customizerList ;
625  delete fclIter ;
626  return (RooSimultaneous*) _ws->pdf(simPdf->GetName()) ;
627 }
628 
629 
630 
631 ////////////////////////////////////////////////////////////////////////////////
632 /// Construct name of composite split
633 
634 std::string RooSimWSTool::makeSplitName(const RooArgSet& splitCatSet)
635 {
636  string name ;
637 
638  TIterator* iter = splitCatSet.createIterator() ;
639  RooAbsArg* arg ;
640  Bool_t first=kTRUE ;
641  while((arg=(RooAbsArg*)iter->Next())) {
642  if (first) {
643  first=kFALSE;
644  } else {
645  name += "," ;
646  }
647  name += arg->GetName() ;
648  }
649  delete iter ;
650 
651  return name ;
652 }
653 
654 
655 
656 
657 ////////////////////////////////////////////////////////////////////////////////
658 /// Specify that parameters names listed in paramNameList be split in (product of) category(s)
659 /// listed in categoryNameList
660 
661 void RooSimWSTool::SplitRule::splitParameter(const char* paramNameList, const char* categoryNameList)
662 {
663  char paramBuf[4096] ;
664  char catBuf[4096] ;
665  strlcpy(paramBuf,paramNameList,4096) ;
666  strlcpy(catBuf,categoryNameList,4096) ;
667 
668  // First parse category list
669  list<string> catList ;
670  char* cat = strtok(catBuf,"{,}") ;
671  while(cat) {
672  catList.push_back(cat) ;
673  cat = strtok(0,"{,}") ;
674  }
675 
676  // Now parse parameter list
677  char* param = strtok(paramBuf,"{,}") ;
678  while(param) {
679  _paramSplitMap[param] = pair<list<string>,string>(catList,"") ;
680  param = strtok(0,"{,}") ;
681  }
682 }
683 
684 
685 ////////////////////////////////////////////////////////////////////////////////
686 /// Specify that parameters names listed in paramNameList be split in constrained way in (product of) category(s)
687 /// listed in categoryNameList and that remainder fraction formula be put in state with name remainderStateName
688 
689 void RooSimWSTool::SplitRule::splitParameterConstrained(const char* paramNameList, const char* categoryNameList, const char* remainderStateName)
690 {
691  char paramBuf[4096] ;
692  char catBuf[4096] ;
693  strlcpy(paramBuf,paramNameList,4096) ;
694  strlcpy(catBuf,categoryNameList,4096) ;
695 
696  // First parse category list
697  list<string> catList ;
698  char* cat = strtok(catBuf,"{,}") ;
699  while(cat) {
700  catList.push_back(cat) ;
701  cat = strtok(0,"{,}") ;
702  }
703 
704  // Now parse parameter list
705  char* param = strtok(paramBuf,"{,}") ;
706  while(param) {
707  _paramSplitMap[param] = pair<list<string>,string>(catList,remainderStateName) ;
708  param = strtok(0,"{,}") ;
709  }
710 }
711 
712 
713 ////////////////////////////////////////////////////////////////////////////////
714 /// Construct the SplitRule object from a list of named arguments past to RooSimWSTool::build
715 /// This method parses any SplitParam and SplitParamComstrained argument in the list
716 
717 void RooSimWSTool::SplitRule::configure(const RooCmdArg& arg1,const RooCmdArg& arg2,const RooCmdArg& arg3,
718  const RooCmdArg& arg4, const RooCmdArg& arg5,const RooCmdArg& arg6)
719 {
720  list<const RooCmdArg*> cmdList ;
721  cmdList.push_back(&arg1) ; cmdList.push_back(&arg2) ;
722  cmdList.push_back(&arg3) ; cmdList.push_back(&arg4) ;
723  cmdList.push_back(&arg5) ; cmdList.push_back(&arg6) ;
724 
725  list<const RooCmdArg*>::iterator iter ;
726  for (iter=cmdList.begin() ; iter!=cmdList.end() ; ++iter) {
727 
728  if ((*iter)->opcode()==0) continue ;
729 
730  string name = (*iter)->opcode() ;
731 
732  if (name=="SplitParam") {
733  splitParameter((*iter)->getString(0),(*iter)->getString(1)) ;
734  } else if (name=="SplitParamConstrained") {
735  splitParameterConstrained((*iter)->getString(0),(*iter)->getString(1),(*iter)->getString(2)) ;
736  }
737  }
738 }
739 
740 
741 
742 
743 ////////////////////////////////////////////////////////////////////////////////
744 /// Add prototype p.d.f pdfName to build configuration with associated split rules 'sr'
745 
746 RooSimWSTool::BuildConfig::BuildConfig(const char* pdfName, SplitRule& sr)
747 {
748  internalAddPdf(pdfName,"",sr) ;
749 }
750 
751 
752 ////////////////////////////////////////////////////////////////////////////////
753 /// Construct build configuration from single prototype 'pdfName' and list of arguments
754 /// that can be passed to RooSimWSTool::build() method. This routine parses SplitParam()
755 /// SplitParamConstrained() and Restrict() arguments.
756 
757 RooSimWSTool::BuildConfig::BuildConfig(const char* pdfName, const RooCmdArg& arg1,const RooCmdArg& arg2,
758  const RooCmdArg& arg3,const RooCmdArg& arg4, const RooCmdArg& arg5,const RooCmdArg& arg6)
759 {
760  SplitRule sr(pdfName) ;
761  sr.configure(arg1,arg2,arg3,arg4,arg5,arg6) ;
762  internalAddPdf(pdfName,"",sr) ;
763  _conflProtocol = RooFit::RenameConflictNodes(pdfName) ;
764 
765  list<const RooCmdArg*> cmdList ;
766  cmdList.push_back(&arg1) ; cmdList.push_back(&arg2) ;
767  cmdList.push_back(&arg3) ; cmdList.push_back(&arg4) ;
768  cmdList.push_back(&arg5) ; cmdList.push_back(&arg6) ;
769 
770  list<const RooCmdArg*>::iterator iter ;
771  for (iter=cmdList.begin() ; iter!=cmdList.end() ; ++iter) {
772  if ((*iter)->opcode()==0) continue ;
773  string name = (*iter)->opcode() ;
774  if (name=="Restrict") {
775  restrictBuild((*iter)->getString(0),(*iter)->getString(1)) ;
776  }
777  if (name=="RenameConflictNodes") {
778  _conflProtocol = *(*iter) ;
779  }
780  }
781 }
782 
783 
784 ////////////////////////////////////////////////////////////////////////////////
785 /// Constructor to make BuildConfig from legacy RooSimPdfBuilder configuration
786 /// Empty for now
787 
788 RooSimWSTool::BuildConfig::BuildConfig(const RooArgSet& /*legacyBuildConfig*/)
789 {
790 }
791 
792 
793 ////////////////////////////////////////////////////////////////////////////////
794 /// Internal routine to add prototype pdf 'pdfName' with list of associated master states 'miStateNameList
795 /// and split rules 'sr' to configuration
796 
797 void RooSimWSTool::BuildConfig::internalAddPdf(const char* pdfName, const char* miStateNameList,SplitRule& sr)
798 {
799  char buf[4096] ;
800  strlcpy(buf,miStateNameList,4096) ;
801 
802  char* tok = strtok(buf,",") ;
803  while(tok) {
804  sr._miStateNameList.push_back(tok) ;
805  tok = strtok(0,",") ;
806  }
807 
808  _pdfmap[pdfName] = sr ;
809 }
810 
811 
812 ////////////////////////////////////////////////////////////////////////////////
813 /// Restrict build by only considering state names in stateList for split in category catName
814 
815 void RooSimWSTool::BuildConfig::restrictBuild(const char* catName, const char* stateList)
816 {
817  _restr[catName] = stateList ;
818 }
819 
820 
821 
822 
823 ////////////////////////////////////////////////////////////////////////////////
824 /// Construct MultiBuildConfig for build configuration with multiple prototype p.d.f.s
825 /// masterIndexCat is the name of the master index category that decides which
826 /// prototype is used.
827 
828 RooSimWSTool::MultiBuildConfig::MultiBuildConfig(const char* masterIndexCat)
829 {
830  _masterCatName = masterIndexCat ;
831 }
832 
833 
834 
835 ////////////////////////////////////////////////////////////////////////////////
836 /// Add protytpe p.d.f 'pdfName' to MultiBuildConfig associated with master indes states 'miStateList'. This
837 /// method parses the SplitParam() and SplitParamConstrained() arguments
838 
839 void RooSimWSTool::MultiBuildConfig::addPdf(const char* miStateList, const char* pdfName, const RooCmdArg& arg1,const RooCmdArg& arg2,
840  const RooCmdArg& arg3,const RooCmdArg& arg4, const RooCmdArg& arg5,const RooCmdArg& arg6)
841 {
842  SplitRule sr(pdfName) ;
843  sr.configure(arg1,arg2,arg3,arg4,arg5,arg6) ;
844  internalAddPdf(pdfName,miStateList,sr) ;
845 }
846 
847 
848 
849 ////////////////////////////////////////////////////////////////////////////////
850 /// Add protytpe p.d.f 'pdfName' to MultiBuildConfig associated with master indes states 'miStateList'.
851 
852 void RooSimWSTool::MultiBuildConfig::addPdf(const char* miStateList, const char* pdfName, SplitRule& sr)
853 {
854  internalAddPdf(pdfName,miStateList,sr) ;
855 }
856 
857 
858 
859 
860 ////////////////////////////////////////////////////////////////////////////////
861 /// Destructor
862 
863 RooSimWSTool::ObjSplitRule::~ObjSplitRule()
864 {
865 }
866 
867 
868 
869 
870 ////////////////////////////////////////////////////////////////////////////////
871 /// Print details of a validated build configuration
872 
873 void RooSimWSTool::ObjBuildConfig::print()
874 {
875  // --- Dump contents of object build config ---
876  map<RooAbsPdf*,ObjSplitRule>::iterator ri ;
877  for (ri = _pdfmap.begin() ; ri != _pdfmap.end() ; ++ri ) {
878  cout << "Splitrule for p.d.f " << ri->first->GetName() << " with state list " ;
879  for (std::list<const RooCatType*>::iterator misi= ri->second._miStateList.begin() ; misi!=ri->second._miStateList.end() ; ++misi) {
880  cout << (*misi)->GetName() << " " ;
881  }
882  cout << endl ;
883 
884  map<RooAbsArg*,pair<RooArgSet,string> >::iterator csi ;
885  for (csi = ri->second._paramSplitMap.begin() ; csi != ri->second._paramSplitMap.end() ; ++csi ) {
886  if (csi->second.second.length()>0) {
887  cout << " parameter " << csi->first->GetName() << " is split with constraint in categories " << csi->second.first
888  << " with remainder in state " << csi->second.second << endl ;
889  } else {
890  cout << " parameter " << csi->first->GetName() << " is split with constraint in categories " << csi->second.first << endl ;
891  }
892  }
893  }
894 
895  map<RooAbsCategory*,list<const RooCatType*> >::iterator riter ;
896  for (riter=_restr.begin() ; riter!=_restr.end() ; ++riter) {
897  cout << "Restricting build in category " << riter->first->GetName() << " to states " ;
898  list<const RooCatType*>::iterator i ;
899  for (i=riter->second.begin() ; i!=riter->second.end() ; ++i) {
900  if (i!=riter->second.begin()) cout << "," ;
901  cout << (*i)->GetName() ;
902  }
903  cout << endl ;
904  }
905 
906 }
907 
908 
909 
910 
911 ////////////////////////////////////////////////////////////////////////////////
912 
913 std::string RooSimWSTool::SimWSIFace::create(RooFactoryWSTool& ft, const char* typeName, const char* instanceName, std::vector<std::string> args)
914 {
915  string tn(typeName) ;
916  if (tn=="SIMCLONE") {
917 
918  // Perform syntax check. Warn about any meta parameters other than $SplitParam, $SplitParamConstrained, $Restrict and $Verbose
919  for (unsigned int i=1 ; i<args.size() ; i++) {
920  if (args[i].find("$SplitParam(")!=0 &&
921  args[i].find("$SplitParamConstrained(")!=0 &&
922  args[i].find("$SplitRestrict(")!=0 &&
923  args[i].find("$Verbose(")!=0) {
924  throw string(Form("RooSimWSTool::SimWSIFace::create() ERROR: unknown token %s encountered",args[i].c_str())) ;
925  }
926  }
927 
928  // Make SplitRule object from $SplitParam and $SplitParamConstrained arguments
929  RooSimWSTool::SplitRule sr(args[0].c_str()) ;
930  for (unsigned int i=1 ; i<args.size() ; i++) {
931  if (args[i].find("$SplitParam(")==0) {
932  vector<string> subargs = ft.splitFunctionArgs(args[i].c_str()) ;
933  if (subargs.size()!=2) {
934  throw string(Form("Incorrect number of arguments in $SplitParam, have %d, expect 2",(Int_t)subargs.size())) ;
935  }
936  sr.splitParameter(subargs[0].c_str(),subargs[1].c_str()) ;
937  } else if (args[i].find("$SplitParamConstrained(")==0) {
938  vector<string> subargs = ft.splitFunctionArgs(args[i].c_str()) ;
939  if (subargs.size()!=3) {
940  throw string(Form("Incorrect number of arguments in $SplitParamConstrained, have %d, expect 3",(Int_t)subargs.size())) ;
941  }
942  sr.splitParameterConstrained(subargs[0].c_str(), subargs[1].c_str(), subargs[2].c_str()) ;
943  }
944  }
945 
946  // Make BuildConfig object
947  RooSimWSTool::BuildConfig bc(args[0].c_str(),sr) ;
948  for (unsigned int i=1 ; i<args.size() ; i++) {
949  if (args[i].find("$Restrict(")==0) {
950  vector<string> subargs = ft.splitFunctionArgs(args[i].c_str()) ;
951  if (subargs.size()!=2) {
952  throw string(Form("Incorrect number of arguments in $Restrict, have %d, expect 2",(Int_t)subargs.size())) ;
953  }
954  bc.restrictBuild(subargs[0].c_str(),subargs[1].c_str()) ;
955  }
956  }
957 
958  // Look for verbose flag
959  Bool_t verbose(kFALSE) ;
960  for (unsigned int i=1 ; i<args.size() ; i++) {
961  if (args[i].find("$Verbose(")==0) {
962  vector<string> subargs = ft.splitFunctionArgs(args[i].c_str()) ;
963  if (subargs.size()>0) {
964  verbose = atoi(subargs[0].c_str()) ;
965  }
966  }
967  }
968 
969  // Build pdf clone
970  RooSimWSTool sct(ft.ws()) ;
971  RooAbsPdf* pdf = sct.build(instanceName,bc,verbose) ;
972  if (!pdf) {
973  throw string(Form("RooSimWSTool::SimWSIFace::create() error in RooSimWSTool::build() for %s",instanceName)) ;
974  }
975 
976  // Import into workspace
977  ft.ws().import(*pdf,RooFit::Silence()) ;
978 
979  } else if (tn=="MSIMCLONE") {
980 
981  // First make a multibuild config from the master index cat
982  RooSimWSTool::MultiBuildConfig mbc(args[0].c_str()) ;
983 
984  for (unsigned int i=1 ; i<args.size() ; i++) {
985  if (args[i].find("$AddPdf(")==0) {
986  // Process an add-pdf operation
987  vector<string> subargs = ft.splitFunctionArgs(args[i].c_str()) ;
988 
989  // Make SplitRule object from $SplitParam and $SplitParamConstrained arguments
990  RooSimWSTool::SplitRule sr(subargs[1].c_str()) ;
991  for (unsigned int j=2 ; j<subargs.size() ; j++) {
992  if (subargs[j].find("$SplitParam(")==0) {
993  vector<string> subsubargs = ft.splitFunctionArgs(subargs[j].c_str()) ;
994  if (subsubargs.size()!=2) {
995  throw string(Form("Incorrect number of arguments in $SplitParam, have %d, expect 2",(Int_t)subsubargs.size())) ;
996  }
997  sr.splitParameter(subsubargs[0].c_str(),subsubargs[1].c_str()) ;
998  } else if (subargs[j].find("$SplitParamConstrained(")==0) {
999  vector<string> subsubargs = ft.splitFunctionArgs(subargs[j].c_str()) ;
1000  if (subsubargs.size()!=3) {
1001  throw string(Form("Incorrect number of arguments in $SplitParamConstrained, have %d, expect 3",(Int_t)subsubargs.size())) ;
1002  }
1003  sr.splitParameterConstrained(subsubargs[0].c_str(), subsubargs[1].c_str(), subsubargs[2].c_str()) ;
1004  }
1005  }
1006  mbc.addPdf(subargs[0].c_str(),subargs[1].c_str(),sr) ;
1007 
1008  } else if (args[i].find("$Restrict(")==0) {
1009 
1010  // Process a restrict operation
1011  vector<string> subargs = ft.splitFunctionArgs(args[i].c_str()) ;
1012  if (subargs.size()!=2) {
1013  throw string(Form("Incorrect number of arguments in $Restrict, have %d, expect 2",(Int_t)subargs.size())) ;
1014  }
1015  mbc.restrictBuild(subargs[0].c_str(),subargs[1].c_str()) ;
1016 
1017  } else {
1018  throw string(Form("RooSimWSTool::SimWSIFace::create() ERROR: unknown token in MSIMCLONE: %s",args[i].c_str())) ;
1019  }
1020  }
1021 
1022  // Build pdf clone
1023  RooSimWSTool sct(ft.ws()) ;
1024  RooAbsPdf* pdf = sct.build(instanceName,mbc,kFALSE) ;
1025  if (!pdf) {
1026  throw string(Form("RooSimWSTool::SimWSIFace::create() error in RooSimWSTool::build() for %s",instanceName)) ;
1027  }
1028 
1029  // Import into workspace
1030  ft.ws().import(*pdf,RooFit::Silence()) ;
1031 
1032 
1033  } else {
1034  throw string(Form("RooSimWSTool::SimWSIFace::create() ERROR: Unknown meta-type %s requested",typeName)) ;
1035  }
1036 
1037  return string(instanceName) ;
1038 }