Belle II Software  light-2205-abys
Manager.cc
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * See git log for contributors and copyright holders. *
6  * This file is licensed under LGPL-3.0, see LICENSE.md. *
7  **************************************************************************/
8 
9 #include <analysis/VariableManager/Manager.h>
10 #include <analysis/dataobjects/Particle.h>
11 
12 #include <framework/logging/Logger.h>
13 #include <framework/utilities/Conversion.h>
14 #include <framework/utilities/GeneralCut.h>
15 
16 #include <boost/algorithm/string.hpp>
17 
18 #include <string>
19 #include <regex>
20 #include <set>
21 
22 using namespace Belle2;
23 
24 Variable::Manager::~Manager() = default;
26 {
27  static Variable::Manager v;
28  return v;
29 }
30 
31 const Variable::Manager::Var* Variable::Manager::getVariable(const std::string& functionName,
32  const std::vector<std::string>& functionArguments)
33 {
34  // Combine to full name for alias resolving
35  std::string fullname = functionName + "(" + boost::algorithm::join(functionArguments, ", ") + ")";
36 
37  // resolve aliases. Aliases might point to other aliases so we need to keep a
38  // set of what we have seen so far to avoid running into infinite loops
39  std::set<std::string> aliasesSeen;
40  for (auto aliasIter = m_alias.find(fullname); aliasIter != m_alias.end(); aliasIter = m_alias.find(fullname)) {
41  const auto [it, added] = aliasesSeen.insert(fullname);
42  if (!added) {
43  B2FATAL("Encountered a loop in the alias definitions between the aliases "
44  << boost::algorithm::join(aliasesSeen, ", "));
45  }
46  fullname = aliasIter->second;
47  }
48  auto mapIter = m_variables.find(fullname);
49  if (mapIter == m_variables.end()) {
50  if (!createVariable(fullname, functionName, functionArguments)) return nullptr;
51  mapIter = m_variables.find(fullname);
52  if (mapIter == m_variables.end()) return nullptr;
53  }
54  return mapIter->second.get();
55 }
56 
58 {
59  // resolve aliases. Aliases might point to other aliases so we need to keep a
60  // set of what we have seen so far to avoid running into infinite loops
61  std::set<std::string> aliasesSeen;
62  for (auto aliasIter = m_alias.find(name); aliasIter != m_alias.end(); aliasIter = m_alias.find(name)) {
63  const auto [it, added] = aliasesSeen.insert(name);
64  if (!added) {
65  B2FATAL("Encountered a loop in the alias definitions between the aliases "
66  << boost::algorithm::join(aliasesSeen, ", "));
67  }
68  name = aliasIter->second;
69  }
70  auto mapIter = m_variables.find(name);
71  if (mapIter == m_variables.end()) {
72  if (!createVariable(name)) return nullptr;
73  mapIter = m_variables.find(name);
74  if (mapIter == m_variables.end()) return nullptr;
75  }
76  return mapIter->second.get();
77 }
78 
79 std::vector<const Variable::Manager::Var*> Variable::Manager::getVariables(const std::vector<std::string>& variables)
80 {
81 
82  std::vector<const Variable::Manager::Var*> variable_pointers;
83  for (auto& variable : variables) {
84  const Var* x = getVariable(variable);
85  if (x == nullptr) {
86  B2WARNING("Couldn't find variable " << variable << " via the Variable::Manager. Check the name!");
87  }
88  variable_pointers.push_back(x);
89  }
90  return variable_pointers;
91 
92 }
93 
94 
95 bool Variable::Manager::addAlias(const std::string& alias, const std::string& variable)
96 {
97 
98  assertValidName(alias);
99 
100  if (m_alias.find(alias) != m_alias.end()) {
101  if (variable == m_alias[alias]) { return true; }
102  B2WARNING("An alias with the name '" << alias << "' exists and is set to '" << m_alias[alias] << "', setting it to '" << variable <<
103  "'. Be aware: only the last alias defined before processing the events will be used!");
104  m_alias[alias] = variable;
105  return true;
106  }
107 
108  if (m_variables.find(alias) != m_variables.end()) {
109  B2ERROR("Variable with the name '" << alias << "' exists already, cannot add it as an alias!");
110  return false;
111  }
112 
113  m_alias.insert(std::make_pair(alias, variable));
114  return true;
115 }
116 
118 {
119  m_alias.clear();
120 }
121 
123 {
124  long unsigned int longest_alias_size = 0;
125  for (const auto& a : m_alias) {
126  if (a.first.length() > longest_alias_size) {
127  longest_alias_size = a.first.length();
128  }
129  }
130  B2INFO("=====================================");
131  B2INFO("The following aliases are registered:");
132  for (const auto& a : m_alias) {
133  B2INFO(std::string(a.first, 0, longest_alias_size) << std::string(longest_alias_size - a.first.length(),
134  ' ') << " --> " << a.second);
135  }
136  B2INFO("=====================================");
137 }
138 
139 bool Variable::Manager::addCollection(const std::string& collection, const std::vector<std::string>& variables)
140 {
141 
142  assertValidName(collection);
143 
144  if (m_collection.find(collection) != m_collection.end()) {
145  B2WARNING("Another collection with the name'" << collection << "' is already set! I overwrite it!");
146  m_collection[collection] = variables;
147  return true;
148  }
149 
150  if (m_variables.find(collection) != m_variables.end()) {
151  B2ERROR("Variable with the name '" << collection << "' exists already, won't add it as an collection!");
152  return false;
153  }
154 
155  m_collection.insert(std::make_pair(collection, variables));
156  return true;
157 }
158 
159 
160 std::vector<std::string> Variable::Manager::getCollection(const std::string& collection)
161 {
162 
163  return m_collection[collection];
164 
165 }
166 
167 std::vector<std::string> Variable::Manager::resolveCollections(const std::vector<std::string>& variables)
168 {
169 
170  std::vector<std::string> temp;
171 
172  for (const auto& var : variables) {
173  auto it = m_collection.find(var);
174  if (it != m_collection.end()) {
175  temp.insert(temp.end(), it->second.begin(), it->second.end());
176  } else {
177  temp.push_back(var);
178  }
179  }
180  return temp;
181 
182 }
183 
184 
185 void Variable::Manager::assertValidName(const std::string& name)
186 {
187  const static std::regex allowedNameRegex("^[a-zA-Z0-9_]*$");
188 
189  if (!std::regex_match(name, allowedNameRegex)) {
190  B2FATAL("Variable '" << name <<
191  "' contains forbidden characters! Only alphanumeric characters plus underscores (_) are allowed for variable names.");
192  }
193 }
194 
195 
196 void Variable::Manager::setVariableGroup(const std::string& groupName)
197 {
198  m_currentGroup = groupName;
199 }
200 
201 bool Variable::Manager::createVariable(const std::string& name)
202 {
203  std::match_results<std::string::const_iterator> results;
204 
205  // Check if name is a simple number
206  if (std::regex_match(name, results, std::regex("^([0-9]+\\.?[0-9]*)$"))) {
207  float float_number = std::stof(results[1]);
208  auto func = [float_number](const Particle*) -> double {
209  return float_number;
210  };
211  m_variables[name] = std::make_shared<Var>(name, func, std::string("Returns number ") + name);
212  return true;
213  }
214 
215  // Check if name is a function call
216  if (std::regex_match(name, results, std::regex("^([a-zA-Z0-9_]*)\\((.*)\\)$"))) {
217 
218  std::string functionName = results[1];
219  boost::algorithm::trim(functionName);
220  std::vector<std::string> functionArguments = splitOnDelimiterAndConserveParenthesis(results[2], ',', '(', ')');
221  for (auto& str : functionArguments) {
222  boost::algorithm::trim(str);
223  }
224 
225  // Search function name in parameter variables
226  auto parameterIter = m_parameter_variables.find(functionName);
227  if (parameterIter != m_parameter_variables.end()) {
228 
229  std::vector<double> arguments;
230  for (auto& arg : functionArguments) {
231  double number = 0;
232  number = Belle2::convertString<double>(arg);
233  arguments.push_back(number);
234  }
235  auto pfunc = parameterIter->second->function;
236  auto func = [pfunc, arguments](const Particle * particle) -> VarVariant { return pfunc(particle, arguments); };
237  m_variables[name] = std::make_shared<Var>(name, func, parameterIter->second->description, parameterIter->second->group,
238  parameterIter->second->variabletype);
239  return true;
240 
241  }
242 
243  // Search function name in meta variables
244  auto metaIter = m_meta_variables.find(functionName);
245  if (metaIter != m_meta_variables.end()) {
246  auto func = metaIter->second->function(functionArguments);
247  m_variables[name] = std::make_shared<Var>(name, func, metaIter->second->description, metaIter->second->group,
248  metaIter->second->variabletype);
249  return true;
250  }
251  }
252  // Try Formula registration with python parser if variable is not a simple identifier (else we get a infinite loop)
253  if (not std::regex_match(name, std::regex("^[a-zA-Z_][a-zA-Z_0-9]*$")) and not name.empty()) {
254  Py_Initialize();
255  try {
256  // Import parser
257  py::object b2parser_namespace = py::import("b2parser");
258  // Parse Expression
259  py::tuple expression_tuple = py::extract<boost::python::tuple>(b2parser_namespace.attr("parse_expression")(name));
260  try {
261  // Compile ExpressionNode
262  std::shared_ptr<const AbstractExpressionNode<Belle2::Variable::Manager>> expression_node =
263  NodeFactory::compile_expression_node<Belle2::Variable::Manager>(expression_tuple);
264  // Create lambda capturing the ExpressionNode
265  Variable::Manager::FunctionPtr func = [expression_node](const Particle * object) -> VarVariant {
266  return expression_node->evaluate(object);
267  };
268  // Put new variable into set of variables
269  m_variables[name] = std::make_shared<Var>(name, func, std::string("Returns expression ") + name);
270  return true;
271  } catch (std::runtime_error& exception) {
272  B2FATAL("Encountered bad variable name '" << name << "'. Maybe you misspelled it?");
273  }
274  } catch (py::error_already_set&) {
275  PyErr_Print();
276  B2FATAL("Parsing error for formula: '" << name << "'");
277  }
278  }
279 
280  B2FATAL("Encountered bad variable name '" << name << "'. Maybe you misspelled it?");
281  return false;
282 }
283 
284 bool Variable::Manager::createVariable(const std::string& fullname, const std::string& functionName,
285  const std::vector<std::string>& functionArguments)
286 {
287  // Search function name in parameter variables
288  auto parameterIter = m_parameter_variables.find(functionName);
289  if (parameterIter != m_parameter_variables.end()) {
290 
291  std::vector<double> arguments;
292  for (auto& arg : functionArguments) {
293  double number = 0;
294  number = Belle2::convertString<double>(arg);
295  arguments.push_back(number);
296  }
297  auto pfunc = parameterIter->second->function;
298  auto func = [pfunc, arguments](const Particle * particle) -> std::variant<double, int, bool> { return pfunc(particle, arguments); };
299  m_variables[fullname] = std::make_shared<Var>(fullname, func, parameterIter->second->description, parameterIter->second->group,
300  parameterIter->second->variabletype);
301  return true;
302 
303  }
304 
305  // Search function fullname in meta variables
306  auto metaIter = m_meta_variables.find(functionName);
307  if (metaIter != m_meta_variables.end()) {
308  auto func = metaIter->second->function(functionArguments);
309  m_variables[fullname] = std::make_shared<Var>(fullname, func, metaIter->second->description, metaIter->second->group,
310  metaIter->second->variabletype);
311  return true;
312  }
313 
314  B2FATAL("Encountered bad variable name '" << fullname << "'. Maybe you misspelled it?");
315  return false;
316 }
317 
318 
320  const std::string& description, const Variable::Manager::VariableDataType& variabletype,
321  const std::string& unit)
322 {
323  if (!f) {
324  B2FATAL("No function provided for variable '" << name << "'.");
325  }
326 
327  assertValidName(name);
328 
329  auto mapIter = m_variables.find(name);
330  if (mapIter == m_variables.end()) {
331 
332  auto var = std::make_shared<Var>(name, f, description, m_currentGroup, variabletype);
333  B2DEBUG(19, "Registered Variable " << name);
334  m_variables[name] = var;
335  m_variablesInRegistrationOrder.push_back(var.get());
336  if (!unit.empty()) {
337  var.get()->extendDescriptionString("\n\n :Unit: " + unit);
338  }
339  } else {
340  B2FATAL("A variable named '" << name << "' was already registered! Note that all variables need a unique name!");
341  }
342 }
343 
345  const std::string& description, const Variable::Manager::VariableDataType& variabletype,
346  const std::string& unit)
347 {
348  if (!f) {
349  B2FATAL("No function provided for variable '" << name << "'.");
350  }
351 
352  auto mapIter = m_parameter_variables.find(name);
353  if (mapIter == m_parameter_variables.end()) {
354  auto var = std::make_shared<ParameterVar>(name, f, description, m_currentGroup, variabletype);
355  std::string rawName = name.substr(0, name.find('('));
356  assertValidName(rawName);
357  B2DEBUG(19, "Registered parameter Variable " << rawName);
358  m_parameter_variables[rawName] = var;
359  m_variablesInRegistrationOrder.push_back(var.get());
360  if (!unit.empty()) {
361  var.get()->extendDescriptionString("\n\n :Unit: " + unit);
362  }
363  } else {
364  B2FATAL("A variable named '" << name << "' was already registered! Note that all variables need a unique name!");
365  }
366 }
367 
369  const std::string& description, const Variable::Manager::VariableDataType& variabletype)
370 {
371  if (!f) {
372  B2FATAL("No function provided for variable '" << name << "'.");
373  }
374 
375  auto mapIter = m_meta_variables.find(name);
376  if (mapIter == m_meta_variables.end()) {
377  auto var = std::make_shared<MetaVar>(name, f, description, m_currentGroup, variabletype);
378  std::string rawName = name.substr(0, name.find('('));
379  assertValidName(rawName);
380  B2DEBUG(19, "Registered meta Variable " << rawName);
381  m_meta_variables[rawName] = var;
382  m_variablesInRegistrationOrder.push_back(var.get());
383  } else {
384  B2FATAL("A variable named '" << name << "' was already registered! Note that all variables need a unique name!");
385  }
386 }
387 
388 void Variable::Manager::deprecateVariable(const std::string& name, bool make_fatal, const std::string& version,
389  const std::string& description)
390 {
391  auto varIter = m_deprecated.find(name);
392  if (varIter == m_deprecated.end())
393  m_deprecated.insert(std::make_pair(name, std::make_pair(make_fatal, description)));
394  else
395  B2FATAL("There seem to be two calls to deprecate the variable: Please remove one.");
396 
397  auto mapIter = m_variables.find(name);
398  if (mapIter != m_variables.end()) {
399  if (make_fatal) {
400  mapIter->second.get()->extendDescriptionString("\n\n.. warning:: ");
401  } else {
402  mapIter->second.get()->extendDescriptionString("\n\n.. note:: ");
403  }
404  mapIter->second.get()->extendDescriptionString(".. deprecated:: " + version + "\n " + description);
405  } else {
406  auto parMapIter = m_parameter_variables.find(name);
407  if (parMapIter != m_parameter_variables.end()) {
408  if (make_fatal) {
409  parMapIter->second.get()->extendDescriptionString("\n\n.. warning:: ");
410  } else {
411  parMapIter->second.get()->extendDescriptionString("\n\n.. note:: ");
412  }
413  parMapIter->second.get()->extendDescriptionString(".. deprecated:: " + version + "\n " + description);
414  } else {
415  auto metaMapIter = m_meta_variables.find(name);
416  if (metaMapIter != m_meta_variables.end()) {
417  if (make_fatal) {
418  metaMapIter->second.get()->extendDescriptionString("\n\n.. warning:: ");
419  } else {
420  metaMapIter->second.get()->extendDescriptionString("\n\n.. note:: ");
421  }
422  metaMapIter->second.get()->extendDescriptionString(".. deprecated:: " + version + "\n " + description);
423  } else {
424  B2FATAL("The variable '" << name << "' is not registered so it makes no sense to try to deprecate it.");
425  }
426  }
427  }
428 
429 }
430 
431 void Variable::Manager::checkDeprecatedVariable(const std::string& name)
432 {
433  auto varIter = m_deprecated.find(name);
434 
435  if (varIter == m_deprecated.end())
436  return;
437  else {
438  bool make_fatal = varIter->second.first;
439  std::string message = varIter->second.second;
440  if (make_fatal)
441  B2FATAL("Variable " << name << " is deprecated. " << message);
442  else
443  B2WARNING("Variable " << name << " is deprecated. " << message);
444  }
445 }
446 
447 
448 std::vector<std::string> Variable::Manager::getNames() const
449 {
450  std::vector<std::string> names;
451  for (const VarBase* var : m_variablesInRegistrationOrder) {
452  names.push_back(var->name);
453  }
454  return names;
455 }
456 
457 std::vector<std::string> Variable::Manager::getAliasNames() const
458 {
459  std::vector<std::string> names;
460  for (auto al : m_alias) names.push_back(al.first);
461  return names;
462 }
463 
464 double Variable::Manager::evaluate(const std::string& varName, const Particle* p)
465 {
466  const Var* var = getVariable(varName);
467  if (!var) {
468  throw std::runtime_error("Variable::Manager::evaluate(): variable '" + varName + "' not found!");
469  }
470 
471  if (var->variabletype == Variable::Manager::VariableDataType::c_double)
472  return std::get<double>(var->function(p));
473  else if (var->variabletype == Variable::Manager::VariableDataType::c_int)
474  return (double)std::get<int>(var->function(p));
475  else if (var->variabletype == Variable::Manager::VariableDataType::c_bool)
476  return (double)std::get<bool>(var->function(p));
477  else return std::numeric_limits<double>::quiet_NaN();
478 }
Class to store reconstructed particles.
Definition: Particle.h:74
Global list of available variables.
Definition: Manager.h:101
std::vector< std::string > getAliasNames() const
Return a list of all variable alias names (in reverse order added).
Definition: Manager.cc:457
std::function< VarVariant(const Particle *)> FunctionPtr
functions stored take a const Particle* and return VarVariant.
Definition: Manager.h:113
std::vector< std::string > resolveCollections(const std::vector< std::string > &variables)
Resolve Collection Returns variable names corresponding to the given collection or if it is not a col...
Definition: Manager.cc:167
const Var * getVariable(std::string name)
Get the variable belonging to the given key.
Definition: Manager.cc:57
std::variant< double, int, bool > VarVariant
NOTE: the python interface is documented manually in analysis/doc/Variables.rst (because we use ROOT ...
Definition: Manager.h:111
void deprecateVariable(const std::string &name, bool make_fatal, const std::string &version, const std::string &description)
Make a variable deprecated.
Definition: Manager.cc:388
std::vector< const Belle2::Variable::Manager::VarBase * > getVariables() const
Return list of all variables (in order registered).
Definition: Manager.h:225
static Manager & Instance()
get singleton instance.
Definition: Manager.cc:25
void printAliases()
Print existing aliases.
Definition: Manager.cc:122
bool createVariable(const std::string &name)
Creates and registers a concrete variable (Var) from a MetaVar, ParameterVar or numeric constant.
Definition: Manager.cc:201
void assertValidName(const std::string &name)
Abort with B2FATAL if name is not a valid name for a variable.
Definition: Manager.cc:185
void clearAliases()
Clear existing aliases.
Definition: Manager.cc:117
std::function< FunctionPtr(const std::vector< std::string > &)> MetaFunctionPtr
meta functions stored take a const std::vector<std::string>& and return a FunctionPtr.
Definition: Manager.h:117
std::vector< std::string > getNames() const
Return list of all variable names (in order registered).
Definition: Manager.cc:448
void setVariableGroup(const std::string &groupName)
All variables registered after VARIABLE_GROUP(groupName) will be added to this group.
Definition: Manager.cc:196
std::vector< std::string > getCollection(const std::string &collection)
Get Collection Returns variable names corresponding to the given collection.
Definition: Manager.cc:160
bool addAlias(const std::string &alias, const std::string &variable)
Add alias Return true if the alias was successfully added.
Definition: Manager.cc:95
bool addCollection(const std::string &collection, const std::vector< std::string > &variables)
Add collection Return true if the collection was successfully added.
Definition: Manager.cc:139
std::function< VarVariant(const Particle *, const std::vector< double > &)> ParameterFunctionPtr
parameter functions stored take a const Particle*, const std::vector<double>& and return VarVariant.
Definition: Manager.h:115
VariableDataType
data type of variables
Definition: Manager.h:122
void registerVariable(const std::string &name, const Manager::FunctionPtr &f, const std::string &description, const Manager::VariableDataType &v, const std::string &unit="")
Register a variable.
Definition: Manager.cc:319
void checkDeprecatedVariable(const std::string &name)
Check if a variable is deprecated.
Definition: Manager.cc:431
double evaluate(const std::string &varName, const Particle *p)
evaluate variable 'varName' on given Particle.
Definition: Manager.cc:464
std::vector< std::string > splitOnDelimiterAndConserveParenthesis(std::string str, char delimiter, char open, char close)
Split into std::vector on delimiter ignoring delimiters between parenthesis.
Definition: CutHelpers.cc:81
Abstract base class for different kinds of events.
Definition: ClusterUtils.h:23
Base class for information common to all types of variables.
Definition: Manager.h:129
A variable returning a floating-point value for a given Particle.
Definition: Manager.h:146