CMS 3D CMS Logo

 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
FormulaEvaluator.cc
Go to the documentation of this file.
1 // -*- C++ -*-
2 //
3 // Package: CommonTools/Utils
4 // Class : FormulaEvaluator
5 //
6 // Implementation:
7 // [Notes on implementation]
8 //
9 // Original Author: Christopher Jones
10 // Created: Thu, 24 Sep 2015 19:07:58 GMT
11 //
12 
13 // system include files
14 #include <cassert>
15 #include <functional>
16 #include <cstdlib>
17 #include <cmath>
18 
19 // user include files
21 #include "formulaEvaluatorBase.h"
30 
31 using namespace reco;
32 
33 namespace {
34  //Formula Parser Code
35  struct EvaluatorInfo {
36  std::unique_ptr<reco::formula::EvaluatorBase> evaluator;
37  int nextParseIndex=0;
38  unsigned int maxNumVariables=0;
39  unsigned int maxNumParameters=0;
40  };
41 
42  class ExpressionElementFinderBase {
43  public:
44  virtual bool checkStart(char) const = 0;
45 
46  virtual EvaluatorInfo createEvaluator(std::string::const_iterator, std::string::const_iterator) const = 0;
47  };
48 
49  std::string::const_iterator findMatchingParenthesis(std::string::const_iterator iBegin, std::string::const_iterator iEnd) {
50  if (iBegin == iEnd) {
51  return iBegin;
52  }
53  if( *iBegin != '(') {
54  return iBegin;
55  }
56  int level = 1;
57  size_t index = 0;
58  for( auto it = iBegin+1; it != iEnd; ++it) {
59  ++index;
60  if (*it == '(') {
61  ++level;
62  } else if (*it == ')') {
63  --level;
64  if (level == 0) {
65  break;
66  }
67  }
68  }
69  return iBegin + index;
70  }
71 
72  class ConstantFinder : public ExpressionElementFinderBase {
73  virtual bool checkStart(char iSymbol) const override final {
74  if( iSymbol == '-' or iSymbol == '.' or std::isdigit(iSymbol) ) {
75  return true;
76  }
77  return false;
78  }
79 
80  virtual EvaluatorInfo createEvaluator(std::string::const_iterator iBegin, std::string::const_iterator iEnd) const override final {
81  EvaluatorInfo info;
82  try {
83  size_t endIndex=0;
84  std::string s(iBegin,iEnd);
85  double value = stod(s, &endIndex);
86 
87  info.nextParseIndex = endIndex;
88  info.evaluator = std::unique_ptr<reco::formula::EvaluatorBase>( new reco::formula::ConstantEvaluator(value));
89  } catch ( std::invalid_argument ) {}
90 
91  return info;
92 
93  }
94  };
95 
96 
97  class ParameterFinder : public ExpressionElementFinderBase {
98  virtual bool checkStart(char iSymbol) const override final {
99  if( iSymbol == '[') {
100  return true;
101  }
102  return false;
103  }
104 
105  virtual EvaluatorInfo createEvaluator(std::string::const_iterator iBegin, std::string::const_iterator iEnd) const override final {
106  EvaluatorInfo info;
107  if(iEnd == iBegin) {
108  return info;
109  }
110  if(*iBegin != '[') {
111  return info;
112  }
113  info.nextParseIndex = 1;
114  try {
115  size_t endIndex=0;
116  std::string s(iBegin+1,iEnd);
117  unsigned long value = stoul(s, &endIndex);
118 
119  if( iBegin+endIndex+1 == iEnd or *(iBegin+1+endIndex) != ']' ) {
120  return info;
121  }
122 
123 
124  info.nextParseIndex = endIndex+2;
125  info.maxNumParameters = value;
126  info.evaluator = std::unique_ptr<reco::formula::EvaluatorBase>( new reco::formula::ParameterEvaluator(value));
127  } catch ( std::invalid_argument ) {}
128 
129  return info;
130 
131  }
132  };
133 
134  class VariableFinder : public ExpressionElementFinderBase {
135  virtual bool checkStart(char iSymbol) const override final {
136  if( iSymbol == 'x' or iSymbol == 'y' or iSymbol == 'z' or iSymbol == 't' ) {
137  return true;
138  }
139  return false;
140  }
141 
142  virtual EvaluatorInfo createEvaluator(std::string::const_iterator iBegin, std::string::const_iterator iEnd) const override final {
143  EvaluatorInfo info;
144  if(iBegin == iEnd) {
145  return info;
146  }
147  unsigned int index = 4;
148  switch (*iBegin) {
149  case 'x':
150  { index = 0; break;}
151  case 'y':
152  { index = 1; break;}
153  case 'z':
154  { index = 2; break;}
155  case 't':
156  {index = 3; break;}
157  }
158  if(index == 4) {
159  return info;
160  }
161  info.nextParseIndex = 1;
162  info.maxNumVariables = index+1;
163  info.evaluator = std::unique_ptr<reco::formula::EvaluatorBase>(new reco::formula::VariableEvaluator(index) );
164  return info;
165  }
166  };
167 
168  class ExpressionFinder;
169 
170  class FunctionFinder : public ExpressionElementFinderBase {
171  public:
172  FunctionFinder(ExpressionFinder const* iEF):
173  m_expressionFinder(iEF) {};
174 
175  virtual bool checkStart(char iSymbol) const override final {
176  return std::isalpha(iSymbol);
177  }
178 
179  virtual EvaluatorInfo createEvaluator(std::string::const_iterator iBegin, std::string::const_iterator iEnd) const override final;
180 
181  private:
182  ExpressionFinder const* m_expressionFinder;
183  };
184 
185 
186  EvaluatorInfo createBinaryOperatorEvaluator( ExpressionFinder const&,
187  std::unique_ptr<reco::formula::EvaluatorBase> iLHS,
188  std::string::const_iterator iBegin,
189  std::string::const_iterator iEnd) ;
190 
191  class ExpressionFinder {
192 
193  public:
194  ExpressionFinder() {
195  m_elements.reserve(4);
196  m_elements.emplace_back(new FunctionFinder{this});
197  m_elements.emplace_back(new ConstantFinder{});
198  m_elements.emplace_back(new ParameterFinder{});
199  m_elements.emplace_back(new VariableFinder{});
200  }
201 
202  bool checkStart(char iChar) const {
203  if ( '(' == iChar or '-' == iChar or '+' ==iChar) {
204  return true;
205  }
206  for( auto const& e : m_elements) {
207  if (e->checkStart(iChar) ) {
208  return true;
209  }
210  }
211  return false;
212  }
213 
214  EvaluatorInfo createEvaluator(std::string::const_iterator iBegin, std::string::const_iterator iEnd) const {
215  EvaluatorInfo leftEvaluatorInfo ;
216  if( iBegin == iEnd) {
217  return leftEvaluatorInfo;
218  }
219  //Start with '+'
220  if (*iBegin == '+' and iEnd -iBegin > 1 and not std::isdigit( *(iBegin+1) ) ) {
221  leftEvaluatorInfo = createEvaluator(iBegin+1, iEnd);
222 
223  //have to account for the '+' we skipped over
224  leftEvaluatorInfo.nextParseIndex +=1;
225  if( nullptr == leftEvaluatorInfo.evaluator.get() ) {
226  return leftEvaluatorInfo;
227  }
228  if( leftEvaluatorInfo.nextParseIndex == iEnd-iBegin) {
229  return leftEvaluatorInfo;
230  }
231  }
232  //Start with '-'
233  else if (*iBegin == '-' and iEnd -iBegin > 1 and not std::isdigit( *(iBegin+1) ) ) {
234  leftEvaluatorInfo = createEvaluator(iBegin+1, iEnd);
235 
236  //have to account for the '+' we skipped over
237  leftEvaluatorInfo.nextParseIndex +=1;
238  if( nullptr == leftEvaluatorInfo.evaluator.get() ) {
239  return leftEvaluatorInfo;
240  }
241  leftEvaluatorInfo.evaluator = std::unique_ptr<reco::formula::EvaluatorBase>( new reco::formula::UnaryMinusEvaluator( std::move(leftEvaluatorInfo.evaluator)) );
242  if( leftEvaluatorInfo.nextParseIndex == iEnd-iBegin) {
243  return leftEvaluatorInfo;
244  }
245  }
246  //Start with '('
247  else if( *iBegin == '(') {
248  auto endParenthesis = findMatchingParenthesis(iBegin,iEnd);
249  if(iBegin== endParenthesis) {
250  return leftEvaluatorInfo;
251  }
252  leftEvaluatorInfo = createEvaluator(iBegin+1,endParenthesis);
253  ++leftEvaluatorInfo.nextParseIndex;
254  if(leftEvaluatorInfo.evaluator.get() == nullptr) {
255  return leftEvaluatorInfo;
256  }
257  //need to account for closing parenthesis
258  ++leftEvaluatorInfo.nextParseIndex;
259  leftEvaluatorInfo.evaluator->setPrecidenceToParenthesis();
260  if( iBegin+leftEvaluatorInfo.nextParseIndex == iEnd) {
261  return leftEvaluatorInfo;
262  }
263  } else {
264  //Does not start with a '('
265  int maxParseDistance = 0;
266  for( auto const& e: m_elements) {
267  if(e->checkStart(*iBegin) ) {
268  leftEvaluatorInfo = e->createEvaluator(iBegin,iEnd);
269  if(leftEvaluatorInfo.evaluator != nullptr) {
270  break;
271  }
272  if (leftEvaluatorInfo.nextParseIndex > maxParseDistance) {
273  maxParseDistance = leftEvaluatorInfo.nextParseIndex;
274  }
275  }
276  }
277  if(leftEvaluatorInfo.evaluator.get() == nullptr) {
278  //failed to parse
279  leftEvaluatorInfo.nextParseIndex = maxParseDistance;
280  return leftEvaluatorInfo;
281  }
282  }
283  //did we evaluate the full expression?
284  if(leftEvaluatorInfo.nextParseIndex == iEnd-iBegin) {
285  return leftEvaluatorInfo;
286  }
287 
288  //see if this is a binary expression
289  auto fullExpression = createBinaryOperatorEvaluator(*this, std::move(leftEvaluatorInfo.evaluator), iBegin+leftEvaluatorInfo.nextParseIndex, iEnd);
290  fullExpression.nextParseIndex +=leftEvaluatorInfo.nextParseIndex;
291  fullExpression.maxNumVariables = std::max(leftEvaluatorInfo.maxNumVariables, fullExpression.maxNumVariables);
292  fullExpression.maxNumParameters = std::max(leftEvaluatorInfo.maxNumParameters, fullExpression.maxNumParameters);
293  if (iBegin + fullExpression.nextParseIndex != iEnd) {
294  //did not parse the full expression
295  fullExpression.evaluator.release();
296  }
297  return fullExpression;
298  }
299 
300  private:
301  std::vector<std::unique_ptr<ExpressionElementFinderBase>> m_elements;
302 
303  };
304 
305  template<typename Op>
306  EvaluatorInfo createBinaryOperatorEvaluatorT(int iSymbolLength,
308  ExpressionFinder const& iEF,
309  std::unique_ptr<reco::formula::EvaluatorBase> iLHS,
310  std::string::const_iterator iBegin,
311  std::string::const_iterator iEnd) {
312  EvaluatorInfo evalInfo = iEF.createEvaluator(iBegin+iSymbolLength,iEnd);
313  evalInfo.nextParseIndex += iSymbolLength;
314 
315  if(evalInfo.evaluator.get() == nullptr) {
316  return evalInfo;
317  }
318 
319  if( static_cast<unsigned int>(iPrec) >= evalInfo.evaluator->precidence() ) {
320  auto b = dynamic_cast<reco::formula::BinaryOperatorEvaluatorBase*>( evalInfo.evaluator.get() );
321  assert(b != nullptr);
322  std::unique_ptr<reco::formula::EvaluatorBase> temp;
323  b->swapLeftEvaluator(temp);
324  std::unique_ptr<reco::formula::EvaluatorBase> op{ new reco::formula::BinaryOperatorEvaluator<Op>(std::move(iLHS), std::move(temp), iPrec) };
325  b->swapLeftEvaluator(op);
326 
327  } else {
328  std::unique_ptr<reco::formula::EvaluatorBase> op{ new reco::formula::BinaryOperatorEvaluator<Op>(std::move(iLHS), std::move(evalInfo.evaluator), iPrec) };
329  evalInfo.evaluator.swap(op);
330  }
331  return evalInfo;
332  }
333 
334  struct power {
335  double operator()(double iLHS, double iRHS) const {
336  return std::pow(iLHS,iRHS);
337  }
338  };
339 
340 
341  EvaluatorInfo
342  createBinaryOperatorEvaluator( ExpressionFinder const& iEF,
343  std::unique_ptr<reco::formula::EvaluatorBase> iLHS,
344  std::string::const_iterator iBegin,
345  std::string::const_iterator iEnd) {
346  EvaluatorInfo evalInfo;
347  if(iBegin == iEnd) {
348  return evalInfo;
349  }
350 
351  if(*iBegin == '+') {
352  return createBinaryOperatorEvaluatorT<std::plus<double>>(1,
354  iEF,
355  std::move(iLHS),
356  iBegin,
357  iEnd);
358  }
359 
360  else if(*iBegin == '-') {
361  return createBinaryOperatorEvaluatorT<std::minus<double>>(1,
363  iEF,
364  std::move(iLHS),
365  iBegin,
366  iEnd);
367  }
368  else if(*iBegin == '*') {
369  return createBinaryOperatorEvaluatorT<std::multiplies<double>>(1,
371  iEF,
372  std::move(iLHS),
373  iBegin,
374  iEnd);
375  }
376  else if(*iBegin == '/') {
377  return createBinaryOperatorEvaluatorT<std::divides<double>>(1,
379  iEF,
380  std::move(iLHS),
381  iBegin,
382  iEnd);
383  }
384 
385  else if(*iBegin == '^') {
386  return createBinaryOperatorEvaluatorT<power>(1,
388  iEF,
389  std::move(iLHS),
390  iBegin,
391  iEnd);
392  }
393 
394  return evalInfo;
395  }
396 
397 
398  template<typename Op>
399  EvaluatorInfo
400  checkForSingleArgFunction(std::string::const_iterator iBegin,
401  std::string::const_iterator iEnd,
402  ExpressionFinder const* iExpressionFinder,
403  const std::string& iName,
404  Op op) {
405  EvaluatorInfo info;
406  if(iName.size()+2 > static_cast<unsigned int>(iEnd-iBegin) ) {
407  return info;
408  }
409  auto pos = iName.find(&(*iBegin), 0,iName.size());
410 
411  if(std::string::npos == pos or *(iBegin+iName.size()) != '(') {
412  return info;
413  }
414 
415  info.nextParseIndex = iName.size()+1;
416 
417  auto itEndParen = findMatchingParenthesis(iBegin+iName.size(),iEnd);
418  if(iBegin+iName.size() == itEndParen) {
419  return info;
420  }
421 
422  auto argEvaluatorInfo = iExpressionFinder->createEvaluator(iBegin+iName.size()+1, itEndParen);
423  info.nextParseIndex += argEvaluatorInfo.nextParseIndex;
424  if(argEvaluatorInfo.evaluator.get() == nullptr or info.nextParseIndex+1 != 1+itEndParen - iBegin) {
425  return info;
426  }
427  //account for closing parenthesis
428  ++info.nextParseIndex;
429 
430  info.evaluator.reset( new reco::formula::FunctionOneArgEvaluator(std::move(argEvaluatorInfo.evaluator),
431  op) );
432  return info;
433  }
434 
435  std::string::const_iterator findCommaNotInParenthesis(std::string::const_iterator iBegin,
436  std::string::const_iterator iEnd ) {
437  int level = 0;
438  std::string::const_iterator it = iBegin;
439  for(; it != iEnd; ++it) {
440  if (*it == '(') {
441  ++level;
442  } else if(*it == ')') {
443  --level;
444  }
445  else if( *it ==',' and level == 0 ) {
446  return it;
447  }
448  }
449 
450  return it;
451  }
452 
453 
454  template<typename Op>
455  EvaluatorInfo
456  checkForTwoArgsFunction(std::string::const_iterator iBegin,
457  std::string::const_iterator iEnd,
458  ExpressionFinder const* iExpressionFinder,
459  const std::string& iName,
460  Op op) {
461  EvaluatorInfo info;
462  if(iName.size()+2 > static_cast<unsigned int>(iEnd-iBegin) ) {
463  return info;
464  }
465  auto pos = iName.find(&(*iBegin), 0,iName.size());
466 
467  if(std::string::npos == pos or *(iBegin+iName.size()) != '(') {
468  return info;
469  }
470 
471  info.nextParseIndex = iName.size()+1;
472 
473  auto itEndParen = findMatchingParenthesis(iBegin+iName.size(),iEnd);
474  if(iBegin+iName.size() == itEndParen) {
475  return info;
476  }
477 
478  auto itComma = findCommaNotInParenthesis(iBegin+iName.size()+1, itEndParen);
479 
480  auto arg1EvaluatorInfo = iExpressionFinder->createEvaluator(iBegin+iName.size()+1, itComma);
481  info.nextParseIndex += arg1EvaluatorInfo.nextParseIndex;
482  if(arg1EvaluatorInfo.evaluator.get() == nullptr or info.nextParseIndex != itComma-iBegin ) {
483  return info;
484  }
485  //account for commas
486  ++info.nextParseIndex;
487 
488  auto arg2EvaluatorInfo = iExpressionFinder->createEvaluator(itComma+1, itEndParen);
489  info.nextParseIndex += arg2EvaluatorInfo.nextParseIndex;
490 
491  if(arg2EvaluatorInfo.evaluator.get() == nullptr or info.nextParseIndex+1 != 1+itEndParen - iBegin) {
492  return info;
493  }
494  //account for closing parenthesis
495  ++info.nextParseIndex;
496 
497  info.evaluator.reset( new reco::formula::FunctionTwoArgsEvaluator(std::move(arg1EvaluatorInfo.evaluator),
498  std::move(arg2EvaluatorInfo.evaluator),
499  op) );
500  return info;
501  }
502 
503  static const std::string k_log("log");
504  static const std::string k_log10("log10");
505  static const std::string k_TMath__Log("TMath::Log");
506  double const kLog10Inv = 1./std::log(10.);
507  static const std::string k_exp("exp");
508  static const std::string k_max("max");
509 
510 
511  EvaluatorInfo
512  FunctionFinder::createEvaluator(std::string::const_iterator iBegin, std::string::const_iterator iEnd) const {
513  EvaluatorInfo info;
514 
515  info = checkForSingleArgFunction(iBegin, iEnd, m_expressionFinder,
516  k_log, [](double iArg)->double { return std::log(iArg); } );
517  if(info.evaluator.get() != nullptr) {
518  return info;
519  }
520 
521  info = checkForSingleArgFunction(iBegin, iEnd, m_expressionFinder,
522  k_TMath__Log, [](double iArg)->double { return std::log(iArg); } );
523  if(info.evaluator.get() != nullptr) {
524  return info;
525  }
526 
527  info = checkForSingleArgFunction(iBegin, iEnd, m_expressionFinder,
528  k_log10, [](double iArg)->double { return std::log(iArg)*kLog10Inv; } );
529  if(info.evaluator.get() != nullptr) {
530  return info;
531  }
532 
533  info = checkForSingleArgFunction(iBegin, iEnd, m_expressionFinder,
534  k_exp, [](double iArg)->double { return std::exp(iArg); } );
535  if(info.evaluator.get() != nullptr) {
536  return info;
537  }
538 
539  info = checkForTwoArgsFunction(iBegin, iEnd, m_expressionFinder,
540  k_max, [](double iArg1, double iArg2)->double { return std::max(iArg1,iArg2); } );
541  if(info.evaluator.get() != nullptr) {
542  return info;
543  }
544 
545  return info;
546  };
547 
548  static ExpressionFinder const s_expressionFinder;
549 
550 }
551 //
552 // constants, enums and typedefs
553 //
554 
555 //
556 // static data member definitions
557 //
558 
559 //
560 // constructors and destructor
561 //
563 {
564  auto info = s_expressionFinder.createEvaluator(iFormula.begin(), iFormula.end());
565 
566  if(info.nextParseIndex != static_cast<int>(iFormula.size()) or info.evaluator.get() == nullptr) {
567  throw cms::Exception("FormulaEvaluatorParseError")<<"While parsing '"<<iFormula<<"' could not parse beyond '"<<std::string(iFormula.begin(),iFormula.begin()+info.nextParseIndex) <<"'";
568  }
569  m_evaluator = std::move(info.evaluator);
570  m_nVariables = info.maxNumVariables;
571  m_nParameters = info.maxNumParameters;
572 }
573 
574 //
575 // const member functions
576 //
577 double
578 FormulaEvaluator::evaluate(double const* iVariables, double const* iParameters) const
579 {
580  return m_evaluator->evaluate(iVariables, iParameters);
581 }
582 
583 void
585  throw cms::Exception("WrongNumVariables")<<"FormulaEvaluator expected at least "<<m_nVariables<<" but was passed only "<<iSize;
586 }
587 void
589  throw cms::Exception("WrongNumParameters")<<"FormulaEvaluator expected at least "<<m_nParameters<<" but was passed only "<<iSize;
590 }
static const TGPicture * info(bool iBackgroundIsBlack)
The Signals That Services Can Subscribe To This is based on ActivityRegistry and is current per Services can connect to the signals distributed by the ActivityRegistry in order to monitor the activity of the application Each possible callback has some defined which we here list in angle e< void, edm::EventIDconst &, edm::Timestampconst & > We also list in braces which AR_WATCH_USING_METHOD_ is used for those or
Definition: Activities.doc:12
assert(m_qm.get())
double evaluate(V const &iVariables, P const &iParameters) const
def move
Definition: eostools.py:508
void throwWrongNumberOfParameters(size_t) const
double b
Definition: hdecay.h:120
FormulaEvaluator(std::string const &iFormula)
tuple level
Definition: testEve_cfg.py:34
std::shared_ptr< formula::EvaluatorBase const > m_evaluator
void throwWrongNumberOfVariables(size_t) const
Power< A, B >::type pow(const A &a, const B &b)
Definition: Power.h:40
tuple log
Definition: cmsBatch.py:347