OPAL (Object Oriented Parallel Accelerator Library) 2024.2
OPAL
matheval.hpp
Go to the documentation of this file.
1// Copyright (C) 2011-2013 Rhys Ulerich
2// Copyright (C) ??? Martin Bauer
3// Copyright (C) 2017 Henri Menke
4//
5// This code borrows heavily from code written by Rhys Ulerich and
6// Martin Bauer. They licensed it under the Mozilla Public License,
7// v. 2.0 and the GNU General Public License (no version info),
8// respectively. I believe that I have made enough contributions and
9// altered this code far enough from the originals that I can
10// relicense it under the Boost Software License.
11//
12// Distributed under the Boost Software License, Version 1.0. (See
13// accompanying file LICENSE_1_0.txt or copy at
14// http://www.boost.org/LICENSE_1_0.txt)
15#pragma once
16
17#include <algorithm>
18#include <cmath>
19#include <functional>
20#include <iterator>
21#include <limits>
22#include <map>
23#include <sstream>
24#include <stdexcept>
25#include <string>
26
27#define BOOST_PHOENIX_STL_TUPLE_H_
28#define BOOST_RESULT_OF_USE_DECLTYPE
29
30#include <boost/math/constants/constants.hpp>
31#include <boost/phoenix.hpp>
32#include <boost/spirit/include/qi.hpp>
33#include <boost/variant.hpp>
34
35
36namespace matheval {
37
38 namespace detail {
39
40 namespace qi = boost::spirit::qi;
41 namespace ascii = boost::spirit::ascii;
42
43 namespace math {
44
62 template < typename T >
63 T sgn(T x) { return (T{0} < x) - (x < T{0}); }
64
66 template < typename T >
67 T isnan(T x) { return std::isnan(x); }
68
70 template < typename T >
71 T isinf(T x) { return std::isinf(x); }
72
74 template < typename T >
75 T deg(T x) { return x*boost::math::constants::radian<T>(); }
76
78 template < typename T >
79 T rad(T x) { return x*boost::math::constants::degree<T>(); }
80
81 }
82
83 // AST
84
85 template < typename real_t > struct unary_op;
86 template < typename real_t > struct binary_op;
87
88 struct nil {};
89
95 template < typename real_t >
96 struct expr_ast
97 {
98 using tree_t = boost::variant<
99 nil // can't happen!
100 , real_t
101 , std::string
102 , boost::recursive_wrapper<expr_ast<real_t>>
103 , boost::recursive_wrapper<binary_op<real_t>>
104 , boost::recursive_wrapper<unary_op<real_t>>
105 >;
106 public:
115
121 expr_ast() : tree(nil{}) {}
122
127 template <typename Expr>
128 expr_ast(Expr other) : tree(std::move(other)) {} // NOLINT
129
131 expr_ast& operator+=(expr_ast const &rhs);
133 expr_ast& operator-=(expr_ast const &rhs);
135 expr_ast& operator*=(expr_ast const &rhs);
137 expr_ast& operator/=(expr_ast const &rhs);
138 };
139
141 template < typename real_t >
142 struct unary_op
143 {
145 using op_t = std::function<real_t(real_t)>;
146
149 : op(std::move(op)), rhs(std::move(rhs))
150 {}
151
156 };
157
159 template < typename real_t >
161 {
163 using op_t = std::function<real_t(real_t,real_t)>;
164
167 : op(std::move(op)), lhs(std::move(lhs)), rhs(std::move(rhs))
168 {}
169
176 };
177
178 template < typename real_t >
180 {
181 tree = binary_op<real_t>(std::plus<real_t>{}, tree, rhs);
182 return *this;
183 }
184 template < typename real_t >
186 {
187 tree = binary_op<real_t>(std::minus<real_t>{}, tree, rhs);
188 return *this;
189 }
190 template < typename real_t >
192 {
193 tree = binary_op<real_t>(std::multiplies<real_t>{}, tree, rhs);
194 return *this;
195 }
196 template < typename real_t >
198 {
199 tree = binary_op<real_t>(std::divides<real_t>{}, tree, rhs);
200 return *this;
201 }
202
208 template < typename real_t >
210 {
211 public:
213 using result_type = real_t;
214
216 using symbol_table_t = std::map<std::string, result_type>;
217
222 explicit eval_ast(symbol_table_t sym) : st(std::move(sym)) {}
223
225 result_type operator()(nil /*unused*/) const { return 0; }
226
228 result_type operator()(result_type n) const { return n; }
229
231 result_type operator()(std::string const &c) const
232 {
233 auto it = st.find(c);
234 if (it == st.end()) {
235 throw std::invalid_argument("Unknown variable " + c); // NOLINT
236 }
237 return it->second;
238 }
239
242 {
243 return boost::apply_visitor(*this, ast.tree);
244 }
245
248 {
249 return tree.op(
250 boost::apply_visitor(*this, tree.lhs.tree),
251 boost::apply_visitor(*this, tree.rhs.tree)
252 );
253 }
254
257 {
258 return tree.op(
259 boost::apply_visitor(*this, tree.rhs.tree)
260 );
261 }
262
263 private:
265 };
266
267 template <typename T> struct holds_alternative_impl {
268 using result_type = bool;
269
270 template <typename U> bool operator()(U const & /*unused*/) const {
271 return std::is_same<U, T>::value;
272 }
273 };
274
275 template <typename T, typename... Ts>
276 bool holds_alternative(boost::variant<Ts...> const &v) {
277 return boost::apply_visitor(holds_alternative_impl<T>(), v);
278 }
279
280 template <typename real_t> struct ConstantFolder {
283
285 result_type operator()(nil /*unused*/) const { return 0; }
286
288 result_type operator()(real_t n) const { return n; }
289
291 result_type operator()(std::string const &c) const { return c; }
292
295 return boost::apply_visitor(*this, ast.tree);
296 }
297
300 auto lhs = boost::apply_visitor(*this, tree.lhs.tree);
301 auto rhs = boost::apply_visitor(*this, tree.rhs.tree);
302
303 /* If both operands are known, we can directly evaluate the function,
304 * else we just update the children with the new expressions. */
306 return tree.op(boost::get<real_t>(lhs), boost::get<real_t>(rhs));
307 }
308 return binary_op<real_t>(tree.op, lhs, rhs);
309 }
310
313 auto rhs = boost::apply_visitor(*this, tree.rhs.tree);
314 /* If the operand is known, we can directly evaluate the function. */
315 if (holds_alternative<real_t>(rhs)) {
316 return tree.op(boost::get<real_t>(rhs));
317 }
318 return unary_op<real_t>(tree.op, rhs);
319 }
320 };
321
322 // Expressions
323
325 template < typename real_t >
326 struct unary_expr_ {
328 template < typename T > struct result { using type = T; };
329
332 expr_ast<real_t> const &rhs) const {
333 return expr_ast<real_t>(unary_op<real_t>(op, rhs));
334 }
335 };
336
338 template < typename real_t >
341 template < typename T > struct result { using type = T; };
342
345 expr_ast<real_t> const &lhs,
346 expr_ast<real_t> const &rhs) const {
347 return expr_ast<real_t>(binary_op<real_t>(op, lhs, rhs));
348 }
349 };
350
354 template < typename Iterator >
355 void operator()(Iterator first, Iterator last,
356 boost::spirit::info const& info) const {
357 std::stringstream msg;
358 msg << "Expected "
359 << info
360 << " at \""
361 << std::string{first, last}
362 << "\"";
363
364 throw std::runtime_error(msg.str()); // NOLINT
365 }
366 };
367
368
369 // Grammar
370
372 template < typename real_t, typename Iterator >
373 struct grammar
374 : qi::grammar<
375 Iterator, expr_ast<real_t>(), ascii::space_type
376 >
377 {
378 private:
380 qi::rule<Iterator, expr_ast<real_t>(), ascii::space_type> expression;
381 qi::rule<Iterator, expr_ast<real_t>(), ascii::space_type> term;
382 qi::rule<Iterator, expr_ast<real_t>(), ascii::space_type> factor;
383 qi::rule<Iterator, expr_ast<real_t>(), ascii::space_type> primary;
384 qi::rule<Iterator, std::string()> variable;
385 public:
388 : boost::spirit::qi::symbols<
389 typename std::iterator_traits<Iterator>::value_type,
390 real_t
391 >
392 {
394 {
395 this->add
396 ("e" , boost::math::constants::e<real_t>() )
397 ("epsilon", std::numeric_limits<real_t>::epsilon())
398 ("phi" , boost::math::constants::phi<real_t>() )
399 ("pi" , boost::math::constants::pi<real_t>() )
400 ;
401 }
403
405 struct ufunc_
406 : boost::spirit::qi::symbols<
407 typename std::iterator_traits<Iterator>::value_type,
408 typename unary_op<real_t>::op_t
409 >
410 {
412 {
413 this->add
414 ("abs" , static_cast<real_t(*)(real_t)>(&std::abs ))
415 ("acos" , static_cast<real_t(*)(real_t)>(&std::acos ))
416 ("acosh" , static_cast<real_t(*)(real_t)>(&std::acosh ))
417 ("asin" , static_cast<real_t(*)(real_t)>(&std::asin ))
418 ("asinh" , static_cast<real_t(*)(real_t)>(&std::asinh ))
419 ("atan" , static_cast<real_t(*)(real_t)>(&std::atan ))
420 ("atanh" , static_cast<real_t(*)(real_t)>(&std::atanh ))
421 ("cbrt" , static_cast<real_t(*)(real_t)>(&std::cbrt ))
422 ("ceil" , static_cast<real_t(*)(real_t)>(&std::ceil ))
423 ("cos" , static_cast<real_t(*)(real_t)>(&std::cos ))
424 ("cosh" , static_cast<real_t(*)(real_t)>(&std::cosh ))
425 ("deg2rad", static_cast<real_t(*)(real_t)>(&math::deg ))
426 ("erf" , static_cast<real_t(*)(real_t)>(&std::erf ))
427 ("erfc" , static_cast<real_t(*)(real_t)>(&std::erfc ))
428 ("exp" , static_cast<real_t(*)(real_t)>(&std::exp ))
429 ("exp2" , static_cast<real_t(*)(real_t)>(&std::exp2 ))
430 ("floor" , static_cast<real_t(*)(real_t)>(&std::floor ))
431 ("isinf" , static_cast<real_t(*)(real_t)>(&math::isinf))
432 ("isnan" , static_cast<real_t(*)(real_t)>(&math::isnan))
433 ("log" , static_cast<real_t(*)(real_t)>(&std::log ))
434 ("log2" , static_cast<real_t(*)(real_t)>(&std::log2 ))
435 ("log10" , static_cast<real_t(*)(real_t)>(&std::log10 ))
436 ("rad2deg", static_cast<real_t(*)(real_t)>(&math::rad ))
437 ("round" , static_cast<real_t(*)(real_t)>(&std::round ))
438 ("sgn" , static_cast<real_t(*)(real_t)>(&math::sgn ))
439 ("sin" , static_cast<real_t(*)(real_t)>(&std::sin ))
440 ("sinh" , static_cast<real_t(*)(real_t)>(&std::sinh ))
441 ("sqrt" , static_cast<real_t(*)(real_t)>(&std::sqrt ))
442 ("tan" , static_cast<real_t(*)(real_t)>(&std::tan ))
443 ("tanh" , static_cast<real_t(*)(real_t)>(&std::tanh ))
444 ("tgamma" , static_cast<real_t(*)(real_t)>(&std::tgamma))
445 ;
446 }
448
450 struct bfunc_
451 : boost::spirit::qi::symbols<
452 typename std::iterator_traits<Iterator>::value_type,
453 typename binary_op<real_t>::op_t
454 >
455 {
457 {
458 this->add
459 ("atan2", static_cast<real_t(*)(real_t,real_t)>(&std::atan2))
460 ("max" , static_cast<real_t(*)(real_t,real_t)>(&std::fmax ))
461 ("min" , static_cast<real_t(*)(real_t,real_t)>(&std::fmin ))
462 ("pow" , static_cast<real_t(*)(real_t,real_t)>(&std::pow ))
463 ;
464 }
466
468 grammar() : grammar::base_type(expression)
469 {
470 using boost::spirit::qi::real_parser;
471 using boost::spirit::qi::real_policies;
472 real_parser<real_t,real_policies<real_t>> real;
473
474 using boost::spirit::lexeme;
475 using boost::spirit::qi::_1;
476 using boost::spirit::qi::_2;
477 using boost::spirit::qi::_3;
478 using boost::spirit::qi::_4;
479 using boost::spirit::qi::_val;
480 using boost::spirit::qi::alpha;
481 using boost::spirit::qi::alnum;
482 using boost::spirit::qi::raw;
483
484 boost::phoenix::function<unary_expr_<real_t>> unary_expr;
485 boost::phoenix::function<binary_expr_<real_t>> binary_expr;
486
487 auto fmod = static_cast<real_t(*)(real_t,real_t)>(&std::fmod);
488 auto pow = static_cast<real_t(*)(real_t,real_t)>(&std::pow);
489
490 expression =
491 term [_val = _1]
492 >> *( ('+' > term [_val += _1])
493 | ('-' > term [_val -= _1])
494 )
495 ;
496
497 term =
498 factor [_val = _1]
499 >> *( ('*' > factor [_val *= _1])
500 | ('/' > factor [_val /= _1])
501 | ('%' > factor [_val = binary_expr(fmod, _val, _1)])
502 )
503 ;
504
505 factor =
506 primary [_val = _1]
507 >> *( ("**" > factor [_val = binary_expr(pow, _val, _1)])
508 )
509 ;
510
511 variable =
512 raw[lexeme[alpha >> *(alnum | '_')]];
513
514 primary =
515 real [_val = _1]
516 | ('(' > expression [_val = _1] > ')')
517 | ('-' > primary [_val = unary_expr(std::negate<real_t>{}, _1)])
518 | ('+' > primary [_val = _1])
519 | (bfunc > '(' > expression > ',' > expression > ')')
520 [_val = binary_expr(_1, _2, _3)]
521 | (ufunc > '(' > expression > ')')
522 [_val = unary_expr(_1, _2)]
523 | constant [_val = _1]
524 | variable [_val = _1]
525 ;
526
527 expression.name("expression");
528 term.name("term");
529 factor.name("factor");
530 variable.name("variable");
531 primary.name("primary");
532
533 using boost::spirit::qi::fail;
534 using boost::spirit::qi::on_error;
535 using boost::phoenix::bind;
536 using boost::phoenix::ref;
537
538 on_error<fail>
539 (
541 bind(ref(err_handler), _3, _2, _4)
542 );
543 }
544 };
545
554 template <typename real_t, typename Iterator>
555 detail::expr_ast<real_t> parse(Iterator first, Iterator last) {
557
558 auto ast = detail::expr_ast<real_t>{};
559
560 bool r = qi::phrase_parse(first, last, g, ascii::space, ast);
561
562 if (!r || first != last) {
563 std::string rest(first, last);
564 throw std::runtime_error("Parsing failed at " + rest); // NOLINT
565 }
566
567 return ast;
568 }
569
570 } // namespace detail
571
572
580 template < typename real_t >
581 class Parser
582 {
584 public:
593 template < typename Iterator >
594 void parse(Iterator first, Iterator last)
595 {
596 ast = detail::parse<real_t>(first, last);
597 }
598
600 void parse(std::string const &str)
601 {
602 parse(str.begin(), str.end());
603 }
604
605 void optimize() {
606 ast.tree = boost::apply_visitor(detail::ConstantFolder<real_t>{}, ast.tree);
607 }
608
614 {
615 detail::eval_ast<real_t> solver(st);
616 return solver(ast);
617 }
618 };
619
620
630 template < typename real_t, typename Iterator >
631 real_t parse(Iterator first, Iterator last,
633 {
634 Parser<real_t> parser;
635 parser.parse(first, last);
636 return parser.evaluate(st);
637 }
638
640 template < typename real_t >
641 real_t parse(std::string const &str,
643 {
644 return parse<real_t>(str.begin(), str.end(), st);
645 }
646
647} // namespace matheval
FLieGenerator< T, N > real(const FLieGenerator< std::complex< T >, N > &)
Take real part of a complex generator.
Tps< T > pow(const Tps< T > &x, int y)
Integer power.
Definition TpsMath.h:76
constexpr double c
The velocity of light in m/s.
Definition Physics.h:45
PETE_TBTree< FnFmod, PETE_Scalar< Vektor< T1, Dim > >, typename T2::PETE_Expr_t > fmod(const Vektor< T1, Dim > &l, const PETE_Expr< T2 > &r)
STL namespace.
real_t parse(Iterator first, Iterator last, typename detail::eval_ast< real_t >::symbol_table_t const &st)
Convenience function.
Definition matheval.hpp:631
detail::expr_ast< real_t > parse(Iterator first, Iterator last)
Parse an expression.
Definition matheval.hpp:555
bool holds_alternative(boost::variant< Ts... > const &v)
Definition matheval.hpp:276
T isinf(T x)
isinf function with adjusted return type
Definition matheval.hpp:71
T isnan(T x)
isnan function with adjusted return type
Definition matheval.hpp:67
T rad(T x)
Convert degrees to radians.
Definition matheval.hpp:79
T sgn(T x)
Sign function.
Definition matheval.hpp:63
T deg(T x)
Convert radians to degrees.
Definition matheval.hpp:75
Store a unary operator and its argument tree.
Definition matheval.hpp:143
op_t op
Stored operator.
Definition matheval.hpp:153
expr_ast< real_t > rhs
Stored argument tree.
Definition matheval.hpp:155
unary_op(op_t op, expr_ast< real_t > rhs)
Save the operator and the argument tree.
Definition matheval.hpp:148
std::function< real_t(real_t)> op_t
Signature of a unary operator: op(x).
Definition matheval.hpp:145
Store a binary operator and its argument trees.
Definition matheval.hpp:161
expr_ast< real_t > lhs
Stored argument tree of first argument.
Definition matheval.hpp:173
binary_op(op_t op, expr_ast< real_t > lhs, expr_ast< real_t > rhs)
Save the operator and the argument trees.
Definition matheval.hpp:166
std::function< real_t(real_t, real_t)> op_t
Signature of a binary operator: op(x,y).
Definition matheval.hpp:163
op_t op
Stored operator.
Definition matheval.hpp:171
expr_ast< real_t > rhs
Stored argument tree of second argument.
Definition matheval.hpp:175
Abstract Syntax Tree.
Definition matheval.hpp:97
expr_ast & operator-=(expr_ast const &rhs)
subtract a tree
Definition matheval.hpp:185
expr_ast()
Default constructor.
Definition matheval.hpp:121
expr_ast & operator/=(expr_ast const &rhs)
Divide by a tree.
Definition matheval.hpp:197
expr_ast & operator*=(expr_ast const &rhs)
Multiply by a tree.
Definition matheval.hpp:191
expr_ast & operator+=(expr_ast const &rhs)
Add a tree.
Definition matheval.hpp:179
boost::variant< nil, real_t, std::string, boost::recursive_wrapper< expr_ast< real_t > >, boost::recursive_wrapper< binary_op< real_t > >, boost::recursive_wrapper< unary_op< real_t > > > tree_t
Definition matheval.hpp:98
expr_ast(Expr other)
Copy constructor.
Definition matheval.hpp:128
tree_t tree
AST storage.
Definition matheval.hpp:114
Evaluate the Abstract Syntax Tree.
Definition matheval.hpp:210
real_t result_type
Necessary typedef for boost::apply_visitor.
Definition matheval.hpp:213
result_type operator()(binary_op< real_t > const &tree) const
Evaluate a binary operator and optionally recurse its operands.
Definition matheval.hpp:247
result_type operator()(result_type n) const
Numbers evaluate to themselves.
Definition matheval.hpp:228
result_type operator()(nil) const
Empty nodes in the tree evaluate to 0.
Definition matheval.hpp:225
result_type operator()(expr_ast< real_t > const &ast) const
Recursively evaluate the AST.
Definition matheval.hpp:241
std::map< std::string, result_type > symbol_table_t
Type of the symbol table.
Definition matheval.hpp:216
result_type operator()(unary_op< real_t > const &tree) const
Evaluate a unary operator and optionally recurse its operand.
Definition matheval.hpp:256
eval_ast(symbol_table_t sym)
Constructor.
Definition matheval.hpp:222
result_type operator()(std::string const &c) const
Variables evaluate to their value in the symbol table.
Definition matheval.hpp:231
result_type operator()(binary_op< real_t > const &tree) const
Evaluate a binary operator and optionally recurse its operands.
Definition matheval.hpp:299
result_type operator()(nil) const
Empty nodes in the tree evaluate to 0.
Definition matheval.hpp:285
result_type operator()(unary_op< real_t > const &tree) const
Evaluate a unary operator and optionally recurse its operand.
Definition matheval.hpp:312
result_type operator()(real_t n) const
Numbers evaluate to themselves.
Definition matheval.hpp:288
typename expr_ast< real_t >::tree_t result_type
Necessary typedef for boost::apply_visitor.
Definition matheval.hpp:282
result_type operator()(std::string const &c) const
Variables do not evaluate.
Definition matheval.hpp:291
result_type operator()(expr_ast< real_t > const &ast) const
Recursively evaluate the AST.
Definition matheval.hpp:294
Unary expression functor.
Definition matheval.hpp:326
expr_ast< real_t > operator()(typename unary_op< real_t >::op_t op, expr_ast< real_t > const &rhs) const
Create a new AST containing the unary function.
Definition matheval.hpp:331
Make boost::phoenix::function happy.
Definition matheval.hpp:328
Binary expression functor.
Definition matheval.hpp:339
expr_ast< real_t > operator()(typename binary_op< real_t >::op_t op, expr_ast< real_t > const &lhs, expr_ast< real_t > const &rhs) const
Create a new AST containing the binary function.
Definition matheval.hpp:344
Make boost::phoenix::function happy.
Definition matheval.hpp:341
Error handler for expectation errors.
Definition matheval.hpp:352
void operator()(Iterator first, Iterator last, boost::spirit::info const &info) const
Throw an exception saying where and why parsing failed.
Definition matheval.hpp:355
Expression Grammar.
Definition matheval.hpp:377
grammar()
Constructor builds the grammar.
Definition matheval.hpp:468
qi::rule< Iterator, expr_ast< real_t >(), ascii::space_type > expression
Definition matheval.hpp:380
qi::rule< Iterator, expr_ast< real_t >(), ascii::space_type > factor
Definition matheval.hpp:382
qi::rule< Iterator, expr_ast< real_t >(), ascii::space_type > primary
Definition matheval.hpp:383
expectation_handler err_handler
Definition matheval.hpp:379
qi::rule< Iterator, expr_ast< real_t >(), ascii::space_type > term
Definition matheval.hpp:381
matheval::detail::grammar::ufunc_ ufunc
matheval::detail::grammar::bfunc_ bfunc
matheval::detail::grammar::constant_ constant
qi::rule< Iterator, std::string()> variable
Definition matheval.hpp:384
Class interface.
Definition matheval.hpp:582
detail::expr_ast< real_t > ast
Definition matheval.hpp:583
void parse(std::string const &str)
Definition matheval.hpp:600
real_t evaluate(typename detail::eval_ast< real_t >::symbol_table_t const &st)
Evaluate the AST with a given symbol table.
Definition matheval.hpp:613
void parse(Iterator first, Iterator last)
Parse an expression.
Definition matheval.hpp:594