Belle II Software development
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
22using namespace Belle2;
23
24Variable::Manager::~Manager() = default;
26{
27 static Variable::Manager v;
28 return v;
29}
30
31const 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
79std::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
95bool 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
139std::string Variable::Manager::resolveAlias(const std::string& alias)
140{
141
142 assertValidName(alias);
143
144 if (m_alias.find(alias) == m_alias.end()) {
145 return alias;
146 } else {
147 return m_alias[alias];
148 }
149}
150
151bool Variable::Manager::addCollection(const std::string& collection, const std::vector<std::string>& variables)
152{
153
154 assertValidName(collection);
155
156 if (m_collection.find(collection) != m_collection.end()) {
157 B2WARNING("Another collection with the name'" << collection << "' is already set! I overwrite it!");
158 m_collection[collection] = variables;
159 return true;
160 }
161
162 if (m_variables.find(collection) != m_variables.end()) {
163 B2ERROR("Variable with the name '" << collection << "' exists already, won't add it as an collection!");
164 return false;
165 }
166
167 m_collection.insert(std::make_pair(collection, variables));
168 return true;
169}
170
171
172std::vector<std::string> Variable::Manager::getCollection(const std::string& collection)
173{
174
175 return m_collection[collection];
176
177}
178
179std::vector<std::string> Variable::Manager::resolveCollections(const std::vector<std::string>& variables)
180{
181
182 std::vector<std::string> temp;
183
184 for (const auto& var : variables) {
185 auto it = m_collection.find(var);
186 if (it != m_collection.end()) {
187 temp.insert(temp.end(), it->second.begin(), it->second.end());
188 } else {
189 temp.push_back(var);
190 }
191 }
192 return temp;
193
194}
195
196
197void Variable::Manager::assertValidName(const std::string& name)
198{
199 const static std::regex allowedNameRegex("^[a-zA-Z0-9_]*$");
200
201 if (!std::regex_match(name, allowedNameRegex)) {
202 B2FATAL("Variable '" << name <<
203 "' contains forbidden characters! Only alphanumeric characters plus underscores (_) are allowed for variable names.");
204 }
205}
206
207
208void Variable::Manager::setVariableGroup(const std::string& groupName)
209{
210 m_currentGroup = groupName;
211}
212
213bool Variable::Manager::createVariable(const std::string& name)
214{
215 std::match_results<std::string::const_iterator> results;
216
217 // Check if name is a simple number
218 if (std::regex_match(name, results, std::regex("^([0-9]+\\.?[0-9]*)$"))) {
219 float float_number = std::stof(results[1]);
220 auto func = [float_number](const Particle*) -> double {
221 return float_number;
222 };
223 m_variables[name] = std::make_shared<Var>(name, func, std::string("Returns number ") + name);
224 return true;
225 }
226
227 // Check if name is a function call
228 if (std::regex_match(name, results, std::regex("^([a-zA-Z0-9_]*)\\((.*)\\)$"))) {
229
230 std::string functionName = results[1];
231 boost::algorithm::trim(functionName);
232 std::vector<std::string> functionArguments = splitOnDelimiterAndConserveParenthesis(results[2], ',', '(', ')');
233 for (auto& str : functionArguments) {
234 boost::algorithm::trim(str);
235 }
236
237 // Search function name in parameter variables
238 auto parameterIter = m_parameter_variables.find(functionName);
239 if (parameterIter != m_parameter_variables.end()) {
240
241 std::vector<double> arguments;
242 for (auto& arg : functionArguments) {
243 double number = 0;
244 number = Belle2::convertString<double>(arg);
245 arguments.push_back(number);
246 }
247 auto pfunc = parameterIter->second->function;
248 auto func = [pfunc, arguments](const Particle * particle) -> VarVariant { return pfunc(particle, arguments); };
249 m_variables[name] = std::make_shared<Var>(name, func, parameterIter->second->description, parameterIter->second->group,
250 parameterIter->second->variabletype);
251 return true;
252
253 }
254
255 // Search function name in meta variables
256 auto metaIter = m_meta_variables.find(functionName);
257 if (metaIter != m_meta_variables.end()) {
258 auto func = metaIter->second->function(functionArguments);
259 m_variables[name] = std::make_shared<Var>(name, func, metaIter->second->description, metaIter->second->group,
260 metaIter->second->variabletype);
261 return true;
262 }
263 }
264 // Try Formula registration with python parser if variable is not a simple identifier (else we get a infinite loop)
265 if (not std::regex_match(name, std::regex("^[a-zA-Z_][a-zA-Z_0-9]*$")) and not name.empty()) {
266 Py_Initialize();
267 try {
268 // Import parser
269 py::object b2parser_namespace = py::import("b2parser");
270 // Parse Expression
271 py::tuple expression_tuple = py::extract<boost::python::tuple>(b2parser_namespace.attr("parse_expression")(name));
272 try {
273 // Compile ExpressionNode
274 std::shared_ptr<const AbstractExpressionNode<Belle2::Variable::Manager>> expression_node =
275 NodeFactory::compile_expression_node<Belle2::Variable::Manager>(expression_tuple);
276 // Create lambda capturing the ExpressionNode
277 Variable::Manager::FunctionPtr func = [expression_node](const Particle * object) -> VarVariant {
278 return expression_node->evaluate(object);
279 };
280 // Put new variable into set of variables
281 m_variables[name] = std::make_shared<Var>(name, func, std::string("Returns expression ") + name);
282 return true;
283 } catch (std::runtime_error& exception) {
284 B2FATAL("Encountered bad variable name '" << name << "'. Maybe you misspelled it?");
285 }
286 } catch (py::error_already_set&) {
287 PyErr_Print();
288 B2FATAL("Parsing error for formula: '" << name << "'");
289 }
290 }
291
292 B2FATAL("Encountered bad variable name '" << name << "'. Maybe you misspelled it?");
293 return false;
294}
295
296bool Variable::Manager::createVariable(const std::string& fullname, const std::string& functionName,
297 const std::vector<std::string>& functionArguments)
298{
299 // Search function name in parameter variables
300 auto parameterIter = m_parameter_variables.find(functionName);
301 if (parameterIter != m_parameter_variables.end()) {
302
303 std::vector<double> arguments;
304 for (auto& arg : functionArguments) {
305 double number = 0;
306 number = Belle2::convertString<double>(arg);
307 arguments.push_back(number);
308 }
309 auto pfunc = parameterIter->second->function;
310 auto func = [pfunc, arguments](const Particle * particle) -> std::variant<double, int, bool> { return pfunc(particle, arguments); };
311 m_variables[fullname] = std::make_shared<Var>(fullname, func, parameterIter->second->description, parameterIter->second->group,
312 parameterIter->second->variabletype);
313 return true;
314
315 }
316
317 // Search function fullname in meta variables
318 auto metaIter = m_meta_variables.find(functionName);
319 if (metaIter != m_meta_variables.end()) {
320 auto func = metaIter->second->function(functionArguments);
321 m_variables[fullname] = std::make_shared<Var>(fullname, func, metaIter->second->description, metaIter->second->group,
322 metaIter->second->variabletype);
323 return true;
324 }
325
326 B2FATAL("Encountered bad variable name '" << fullname << "'. Maybe you misspelled it?");
327 return false;
328}
329
330
332 const std::string& description, const Variable::Manager::VariableDataType& variabletype,
333 const std::string& unit)
334{
335 if (!f) {
336 B2FATAL("No function provided for variable '" << name << "'.");
337 }
338
339 assertValidName(name);
340
341 auto mapIter = m_variables.find(name);
342 if (mapIter == m_variables.end()) {
343
344 auto var = std::make_shared<Var>(name, f, description, m_currentGroup, variabletype);
345 B2DEBUG(19, "Registered Variable " << name);
346 m_variables[name] = var;
347 m_variablesInRegistrationOrder.push_back(var.get());
348 if (!unit.empty()) {
349 var.get()->extendDescriptionString(":Unit: " + unit);
350 }
351 } else {
352 B2FATAL("A variable named '" << name << "' was already registered! Note that all variables need a unique name!");
353 }
354}
355
357 const std::string& description, const Variable::Manager::VariableDataType& variabletype,
358 const std::string& unit)
359{
360 if (!f) {
361 B2FATAL("No function provided for variable '" << name << "'.");
362 }
363
364 auto mapIter = m_parameter_variables.find(name);
365 if (mapIter == m_parameter_variables.end()) {
366 auto var = std::make_shared<ParameterVar>(name, f, description, m_currentGroup, variabletype);
367 std::string rawName = name.substr(0, name.find('('));
368 assertValidName(rawName);
369 B2DEBUG(19, "Registered parameter Variable " << rawName);
370 m_parameter_variables[rawName] = var;
371 m_variablesInRegistrationOrder.push_back(var.get());
372 if (!unit.empty()) {
373 var.get()->extendDescriptionString(":Unit: " + unit);
374 }
375 } else {
376 B2FATAL("A variable named '" << name << "' was already registered! Note that all variables need a unique name!");
377 }
378}
379
381 const std::string& description, const Variable::Manager::VariableDataType& variabletype)
382{
383 if (!f) {
384 B2FATAL("No function provided for variable '" << name << "'.");
385 }
386
387 auto mapIter = m_meta_variables.find(name);
388 if (mapIter == m_meta_variables.end()) {
389 auto var = std::make_shared<MetaVar>(name, f, description, m_currentGroup, variabletype);
390 std::string rawName = name.substr(0, name.find('('));
391 assertValidName(rawName);
392 B2DEBUG(19, "Registered meta Variable " << rawName);
393 m_meta_variables[rawName] = var;
394 m_variablesInRegistrationOrder.push_back(var.get());
395 } else {
396 B2FATAL("A variable named '" << name << "' was already registered! Note that all variables need a unique name!");
397 }
398}
399
400void Variable::Manager::deprecateVariable(const std::string& name, bool make_fatal, const std::string& version,
401 const std::string& description)
402{
403 auto varIter = m_deprecated.find(name);
404 if (varIter == m_deprecated.end())
405 m_deprecated.insert(std::make_pair(name, std::make_pair(make_fatal, description)));
406 else
407 B2FATAL("There seem to be two calls to deprecate the variable: Please remove one.");
408
409 auto mapIter = m_variables.find(name);
410 if (mapIter != m_variables.end()) {
411 if (make_fatal) {
412 mapIter->second.get()->extendDescriptionString("\n\n.. warning:: ");
413 } else {
414 mapIter->second.get()->extendDescriptionString("\n\n.. note:: ");
415 }
416 mapIter->second.get()->extendDescriptionString(".. deprecated:: " + version + "\n " + description);
417 } else {
418 auto parMapIter = m_parameter_variables.find(name);
419 if (parMapIter != m_parameter_variables.end()) {
420 if (make_fatal) {
421 parMapIter->second.get()->extendDescriptionString("\n\n.. warning:: ");
422 } else {
423 parMapIter->second.get()->extendDescriptionString("\n\n.. note:: ");
424 }
425 parMapIter->second.get()->extendDescriptionString(".. deprecated:: " + version + "\n " + description);
426 } else {
427 auto metaMapIter = m_meta_variables.find(name);
428 if (metaMapIter != m_meta_variables.end()) {
429 if (make_fatal) {
430 metaMapIter->second.get()->extendDescriptionString("\n\n.. warning:: ");
431 } else {
432 metaMapIter->second.get()->extendDescriptionString("\n\n.. note:: ");
433 }
434 metaMapIter->second.get()->extendDescriptionString(".. deprecated:: " + version + "\n " + description);
435 } else {
436 B2FATAL("The variable '" << name << "' is not registered so it makes no sense to try to deprecate it.");
437 }
438 }
439 }
440
441}
442
444{
445 auto varIter = m_deprecated.find(name);
446
447 if (varIter == m_deprecated.end())
448 return;
449 else {
450 bool make_fatal = varIter->second.first;
451 std::string message = varIter->second.second;
452 if (make_fatal)
453 B2FATAL("Variable " << name << " is deprecated. " << message);
454 else
455 B2WARNING("Variable " << name << " is deprecated. " << message);
456 }
457}
458
459
460std::vector<std::string> Variable::Manager::getNames() const
461{
462 std::vector<std::string> names;
463 for (const VarBase* var : m_variablesInRegistrationOrder) {
464 names.push_back(var->name);
465 }
466 return names;
467}
468
469std::vector<std::string> Variable::Manager::getAliasNames() const
470{
471 std::vector<std::string> names;
472 for (auto al : m_alias) names.push_back(al.first);
473 return names;
474}
475
476double Variable::Manager::evaluate(const std::string& varName, const Particle* p)
477{
478 const Var* var = getVariable(varName);
479 if (!var) {
480 throw std::runtime_error("Variable::Manager::evaluate(): variable '" + varName + "' not found!");
481 }
482
483 if (var->variabletype == Variable::Manager::VariableDataType::c_double)
484 return std::get<double>(var->function(p));
485 else if (var->variabletype == Variable::Manager::VariableDataType::c_int)
486 return (double)std::get<int>(var->function(p));
487 else if (var->variabletype == Variable::Manager::VariableDataType::c_bool)
488 return (double)std::get<bool>(var->function(p));
489 else return std::numeric_limits<double>::quiet_NaN();
490}
Class to store reconstructed particles.
Definition: Particle.h:75
Global list of available variables.
Definition: Manager.h:101
std::vector< const Belle2::Variable::Manager::VarBase * > getVariables() const
Return list of all variables (in order registered).
Definition: Manager.h:230
std::vector< std::string > getAliasNames() const
Return a list of all variable alias names (in reverse order added).
Definition: Manager.cc:469
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:179
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:400
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:213
void assertValidName(const std::string &name)
Abort with B2FATAL if name is not a valid name for a variable.
Definition: Manager.cc:197
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:460
void setVariableGroup(const std::string &groupName)
All variables registered after VARIABLE_GROUP(groupName) will be added to this group.
Definition: Manager.cc:208
std::string resolveAlias(const std::string &alias)
Resolve alias Return original variable name.
Definition: Manager.cc:139
std::vector< std::string > getCollection(const std::string &collection)
Get Collection Returns variable names corresponding to the given collection.
Definition: Manager.cc:172
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:151
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:331
void checkDeprecatedVariable(const std::string &name)
Check if a variable is deprecated.
Definition: Manager.cc:443
double evaluate(const std::string &varName, const Particle *p)
evaluate variable 'varName' on given Particle.
Definition: Manager.cc:476
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.
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