The C++ Mathematical Expression Toolkit Library (ExprTk) is a simple
to use, easy to integrate and extremely efficient mathematical
expression parsing and evaluation engine. The parsing engine
supports numerous forms of functional and logic processing semantics
and is very easily extendible.
Capabilities
The ExprTk library has the following capabilities:
Control Structures (if-then-else, switch case, while loop, repeat until loop)
Optimization of expressions (constant folding, operator coupling and special functions for arithmetic sequences)
Simple string operations (equalities, inequalities, boolean logic and ranges)
Multiple sequence point and sub expression support
Numeric integration and differentiation
Multiple and custom variable support
User defined variables, constants and function support
Multivariate function composition
Template type support for numeric types(float, double, long double)
Single header implementation, no building required. No external dependencies.
Completely portable (Compiled and executed upon: x86 x86-64, ARMv7/8, POWER6/7 and AVR32)
C++ Mathematical Expression Library License
Free use of the C++ Mathematical Expression Library is permitted under the guidelines and in accordance with the most current
version of the "Common Public License."
Compatability
The C++ Mathematical Expression Library implementation is compatible with the following C++ compilers:
fib_i := fib_i + (x := y + 0 * (fib_i := x + (y := fib_i)))
while(x <= 100) { x := x + 1 }
x <= 'abc123' and (y in 'AString') or ('1x2y3z' != z)
(x like '*123*') or ('a123b' ilike y)
Simple Example 1
The following is an example where a given single variable function is evaluated
between a specified range[-5,+5]. The graph below shows the clamped
(red) and non-clamped (blue) versions of the specified function.
simple_example_01.cpp
template<typename T>
void trig_function()
{
std::string expression_string = "clamp(-1.0,sin(2 * pi * x) + cos(x / 2 * pi),+1.0)";
T x;
exprtk::symbol_table<T> symbol_table;
symbol_table.add_variable("x",x);
symbol_table.add_constants();
exprtk::expression<T> expression;
expression.register_symbol_table(symbol_table);
exprtk::parser<T> parser;
parser.compile(expression_string,expression);
for (x = T(-5.0); x <= T(+5.0); x += 0.001)
{
T y = expression.value();
printf("%19.15f\t%19.15f\n",x,y);
}
}
Simple Example 2
The following example generates a square wave form based on Fourier series
accumulations - 14 harmonics. Sigma-approximation is not applied hence
Gibbs phenomenon based ringing is observed on the edges of the square,
as is demonstrated in the graph below.
simple_example_02.cpp
template<typename T>
void square_wave()
{
std::string expr_string = "a*(4/pi)*"
"((1 /1)*sin( 2*pi*f*t)+(1 /3)*sin( 6*pi*f*t)+"
" (1 /5)*sin(10*pi*f*t)+(1 /7)*sin(14*pi*f*t)+"
" (1 /9)*sin(18*pi*f*t)+(1/11)*sin(22*pi*f*t)+"
" (1/13)*sin(26*pi*f*t)+(1/15)*sin(30*pi*f*t)+"
" (1/17)*sin(34*pi*f*t)+(1/19)*sin(38*pi*f*t)+"
" (1/21)*sin(42*pi*f*t)+(1/23)*sin(46*pi*f*t)+"
" (1/25)*sin(50*pi*f*t)+(1/27)*sin(54*pi*f*t))";
static const T pi = T(3.14159265358979323846);
T f = pi/10.0;
T t = T(0.0);
T a = T(10.0);
exprtk::symbol_table<T> symbol_table;
symbol_table.add_variable("f",f);
symbol_table.add_variable("t",t);
symbol_table.add_variable("a",a);
symbol_table.add_constants();
exprtk::expression<T> expression;
expression.register_symbol_table(symbol_table);
exprtk::parser<T> parser;
parser.compile(expr_string,expression);
const T delta = (4.0*pi)/1000.0;
for (t = -2.0*pi; t <= +2.0*pi; t+=delta)
{
T result = expression.value();
printf("%19.15f\t%19.15f\n",t,result);
}
}
Simple Example 3
The following example evaluates a 5th degree polynomial within the domain [0,1]
with a step size of 1/100th. An interesting side note in the expression is how
the multiplication of the coefficients to the variable 'x' are implied rather
than explicity defined using the multiplication operator '*'
simple_example_03.cpp
template<typename T>
void polynomial()
{
std::string expression_string = "25x^5 - 35x^4 - 15x^3 + 40x^2 - 15x + 1";
T r0 = T(0.0);
T r1 = T(1.0);
T x = T(0.0);
exprtk::symbol_table<T> symbol_table;
symbol_table.add_variable("x",x);
exprtk::expression<T> expression;
expression.register_symbol_table(symbol_table);
exprtk::parser<T> parser;
parser.compile(expression_string,expression);
const T delta = T(1/100.0);
for (x = r0; x <= r1; x += delta)
{
printf("%19.15f\t%19.15f\n",x,expression.value());
}
}
Simple Example 4
The following example generates the first 40 Fibonacci numbers
using a simple iterative method. The example demonstrates the
use of multiple assignment and sequence points, switch statements,
while-loops and recursive composited multi-variate functions.
simple_example_04.cpp
template <typename T>
void fibonacci()
{
typedef exprtk::symbol_table<T> symbol_table_t;
typedef exprtk::expression<T> expression_t;
typedef exprtk::parser<T> parser_t;
typedef exprtk::function_compositor<T> compositor_t;
compositor_t compositor;
//define: fibonacci_impl(x,y,z,w)
compositor
.add("fibonacci_impl",
"switch "
"{ "
" case x == 0 : 0; "
" case x == 1 : 1; "
" default : "
" while ((x := (x - 1)) > 0)"
" { "
" w := z; "
" z := z + y; "
" y := w; "
" z "
" }; "
"} ",
"x","y","z","w");
//define: fibonacci(x)
compositor
.add("fibonacci",
"fibonacci_impl(x,0,1,0)",
"x");
T x = T(0);
symbol_table_t& symbol_table = compositor.symbol_table();
symbol_table.add_constants();
symbol_table.add_variable("x",x);
std::string expression_str = "fibonacci(x)";
expression_t expression;
expression.register_symbol_table(symbol_table);
parser_t parser;
parser.compile(expression_str,expression);
for (std::size_t i = 0; i < 40; ++i)
{
x = i;
T result = expression.value();
printf("fibonacci(%3d) = %10.0f\n",i,result);
}
}
Simple Example 5
The following example demonstrates how one can easily register a custom
user defined function to be used within expression evaluations. In this
example the custom function myfunc takes 2 parameters and returns
a result. At the moment an upper limit of 20 parameters is in place.
simple_example_05.cpp
template<typename T>
struct myfunc : public exprtk::ifunction<T>
{
myfunc()
: exprtk::ifunction<T>(2)
{}
inline T operator()(const T& v1, const T& v2)
{
return T(1) + (v1 * v2) / T(3);
}
};
template<typename T>
void custom_function()
{
typedef exprtk::expression<T> expression_t;
std::string expression_string = "myfunc(sin(x*pi),y/2)";
T x = T(1.0);
T y = T(2.0);
myfunc<T> mf;
exprtk::symbol_table<T> symbol_table;
symbol_table.add_variable("x",x);
symbol_table.add_variable("y",y);
symbol_table.add_function("myfunc",mf);
symbol_table.add_constants();
expression_t expression;
expression.register_symbol_table(symbol_table);
exprtk::parser<T> parser;
parser.compile(expression_string,expression);
T result = expression.value();
}
Simple Example 6
The following example demonstrates how one can evaluate an expression
over multiple vectors. The example evaluates the value of an expression
at the ith element of vectors x and y and assigns the value to the ith
value of vector z.
simple_example_06.cpp
The following example demonstrates how one can create and later on
reference variables via the symbol_table. In the example
a simple boolean expression is evaluated so as to determine its
truth-table.
simple_example_07.cpp
template <typename T>
void logic()
{
typedef exprtk::expression<T> expression_t;
std::string expression_string = "not(A and B) or C";
exprtk::symbol_table<T> symbol_table;
symbol_table.create_variable("A");
symbol_table.create_variable("B");
symbol_table.create_variable("C");
expression_t expression;
expression.register_symbol_table(symbol_table);
exprtk::parser<T> parser;
parser.compile(expression_string,expression);
printf(" # | A | B | C | %s\n"
"---+---+---+---+-%s\n",
expression_string.c_str(),
std::string(expression_string.size(),'-').c_str());
for (int i = 0; i < 8; ++i)
{
symbol_table.get_variable("A")->ref() = T(i & 0x01 ? 1 : 0);
symbol_table.get_variable("B")->ref() = T(i & 0x02 ? 1 : 0);
symbol_table.get_variable("C")->ref() = T(i & 0x04 ? 1 : 0);
int result = static_cast<int>(expression.value());
printf(" %d | %d | %d | %d | %d \n",
i,
static_cast<int>(symbol_table.get_variable("A")->value()),
static_cast<int>(symbol_table.get_variable("B")->value()),
static_cast<int>(symbol_table.get_variable("C")->value()),
result);
}
}
Expected output:
# | A | B | C | not(A and B) or C
---+---+---+---+------------------
0 | 0 | 0 | 0 | 1
1 | 1 | 0 | 0 | 1
2 | 0 | 1 | 0 | 1
3 | 1 | 1 | 0 | 0
4 | 0 | 0 | 1 | 1
5 | 1 | 0 | 1 | 1
6 | 0 | 1 | 1 | 1
7 | 1 | 1 | 1 | 1
Simple Example 8
The following example demonstrates the function composition
capabilities within ExprTk. In the example there are two
simple functions defined, an f(x) and a multivariate
g(x,y). The function g(x,y) is composed of calls to
f(x), the culmination of which is a final expression composed
from both functions. Furthermore the example demonstrates how
one can extract all errors that were encountered during a
failed compilation process.
simple_example_08.cpp
template <typename T>
void composite()
{
typedef exprtk::symbol_table<T> symbol_table_t;
typedef exprtk::expression<T> expression_t;
typedef exprtk::parser<T> parser_t;
typedef exprtk::parser_error::type error_t;
typedef exprtk::function_compositor<T> compositor_t;
compositor_t compositor;
T x = T(1);
T y = T(2);
symbol_table_t& symbol_table = compositor.symbol_table();
symbol_table.add_constants();
symbol_table.add_variable("x",x);
symbol_table.add_variable("y",y);
compositor.add("f","sin(x/pi)","x"); // f(x) = sin(x/pi)
compositor.add("g","3*(f(x)+f(y))","x","y"); // g(x,y) = 3(f(x)+f(y))
std::string expression_string = "g(1 + f(x),f(y) / 2)";
expression_t expression;
expression.register_symbol_table(symbol_table);
parser_t parser;
if (!parser.compile(expression_string,expression))
{
printf("Error: %s\tExpression: %s\n",
parser.error().c_str(),
expression_string.c_str());
for (std::size_t i = 0; i < parser.error_count(); ++i)
{
error_t error = parser.get_error(i);
printf("Error: %02d Position: %02d Type: [%14s] Msg: %s\tExpression: %s\n",
i,
error.token.position,
exprtk::parser_error::to_str(error.mode).c_str(),
error.diagnostic.c_str(),
expression_string.c_str());
}
return;
}
T result = expression.value();
printf("%s = %e\n",
expression_string.c_str(),
result);
}
Simple Example 9
The following example demonstrates the computation of prime numbers via a
mixture of recursive composited functions, switch-statement and while-loop
functionalities.
simple_example_09.cpp
template <typename T>
void primes()
{
typedef exprtk::symbol_table<T> symbol_table_t;
typedef exprtk::expression<T> expression_t;
typedef exprtk::parser<T> parser_t;
typedef exprtk::function_compositor<T> compositor_t;
T x = T(0);
exprtk::symbol_table<T> symbol_table;
symbol_table.add_constants();
symbol_table.add_variable("x",x);
compositor_t compositor(symbol_table);
//Method 1 - Recursive if-statement based
compositor
.add("is_prime_impl1",
"if(y == 1,true, "
" if(0 == (x % y),false, "
" is_prime_impl1(x,y-1)))",
"x","y");
compositor
.add("is_prime1",
"if(frac(x) != 0, false, "
" if(x <= 0, false, "
" is_prime_impl1(x,min(x - 1,trunc(sqrt(x)) + 1))))",
"x");
//Method 2 - Recursive switch statement based
compositor
.add("is_prime_impl2",
"switch "
"{ "
" case y == 1 : true; "
" case (x % y) == 0 : false; "
" default : is_prime_impl2(x,y - 1);"
"} ",
"x","y");
compositor
.add("is_prime2",
"switch "
"{ "
" case x <= 0 : false; "
" case frac(x) != 0 : false; "
" default : "
" is_prime_impl2(x,min(x - 1,trunc(sqrt(x)) + 1));"
"} ",
"x");
//Method 3 - Iterative switch statement and while-loop based
compositor
.add("is_prime_impl3",
"while (y > 0) "
"{ "
" switch "
" { "
" case y == 1 : ~(y := 0, true);"
" case (x % y) == 0 : ~(y := 0,false);"
" default : y := y - 1; "
" } "
"} ",
"x","y");
compositor
.add("is_prime3",
"switch "
"{ "
" case x <= 0 : false; "
" case frac(x) != 0 : false; "
" default : "
" is_prime_impl3(x,min(x - 1,trunc(sqrt(x)) + 1));"
"} ",
"x");
std::string expression_str1 = "is_prime1(x)";
std::string expression_str2 = "is_prime2(x)";
std::string expression_str3 = "is_prime3(x)";
expression_t expression1;
expression_t expression2;
expression_t expression3;
expression1.register_symbol_table(symbol_table);
expression2.register_symbol_table(symbol_table);
expression3.register_symbol_table(symbol_table);
exprtk::parser<T> parser;
parser.compile(expression_str1,expression1);
parser.compile(expression_str2,expression2);
parser.compile(expression_str3,expression3);
for (std::size_t i = 0; i < 100; ++i)
{
x = i;
T result1 = expression1.value();
T result2 = expression2.value();
T result3 = expression3.value();
printf("%03d Result1: %c Result2: %c Result3: %c\n",
i,
(result1 == T(1)) ? 'T' : 'F',
(result2 == T(1)) ? 'T' : 'F',
(result3 == T(1)) ? 'T' : 'F');
}
}
Simple Example 10
The following example is an implementation of the Newton–Raphson method
for computing the approximate of the square root of a real number. The
example below demonstrates the use of multiple sub-expressions, sequence
points, switch statements and the repeat until loop.
simple_example_10.cpp
template <typename T>
void newton_sqrt()
{
typedef exprtk::symbol_table<T> symbol_table_t;
typedef exprtk::expression<T> expression_t;
typedef exprtk::parser<T> parser_t;
typedef exprtk::function_compositor<T> compositor_t;
T x = T(0);
exprtk::symbol_table<T> symbol_table;
symbol_table.add_constants();
symbol_table.add_variable("x",x);
compositor_t compositor(symbol_table);
compositor
.add("newton_sqrt_impl",
"switch "
"{ "
" case x < 0 : -inf; "
" case x == 0 : 0; "
" case x == 1 : 1; "
" default: "
" ~{ "
" z := 100; "
" y := x / 2; "
" repeat "
" if (equal(y * y,x), z := 0, 0);"
" y := (1 / 2) * (y + (x / y)); "
" until ((z := (z - 1)) <= 0) "
" }; "
"} ",
"x","y","z");
compositor
.add("newton_sqrt",
"newton_sqrt_impl(x,0,0)","x");
std::string expression_str = "newton_sqrt(x)";
expression_t expression;
expression.register_symbol_table(symbol_table);
exprtk::parser<T> parser;
parser.compile(expression_str,expression);
for (std::size_t i = 0; i < 100; ++i)
{
x = i;
T result = expression.value();
printf("sqrt(%03d) - Result: %12.10f\tReal: %12.10f\n",
static_cast<unsigned int>(i),
result,
std::sqrt(x));
}
}
Benchmarks
The chart below depicts the rate of expression evaluations per
second for an assortment of expressions as denoted.
Each expression is specialised upon the 'double' floating point
type and comprised of two variables that are varied before each
expression evaluation. The expressions are evaluated in two
modes: ExprTk compiled and native optimised.
The benchmark itself was compiled using GCC 4.8 with O3, PGO and
native architecture target compiler settings, and executed upon a
64-Bit Intel Quad Core Extreme i7-920XM 2.0GHz, 16GB RAM, Ubuntu
10.10 kernel 2.6.35 system.