Description

The C++ Mathematical Expression Toolkit Library (ExprTk) is a simple to use, easy to integrate and extremely efficient run-time mathematical expression parser and evaluation engine. ExprTk supports numerous forms of functional, logical and vector processing semantics and is very easily extendible.

Capabilities

The ExprTk library has the following capabilities:

Mathematical operators (+, -, *, /, %, ^)





Functions (min, max, avg, sum, abs, ceil, floor, round, roundn, exp, log, log10, logn, pow, root, sqrt, clamp, inrange, swap)





Trigonometry (sin, cos, tan, acos, asin, atan, atan2, cosh, cot, csc, sec, sinh, tanh, d2r, r2d, d2g, g2d, hyp)





Equalities & Inequalities (=, ==, <>, !=, <, <=, >, >=)





Assignment (:=, +=, -=, *=, /=, %=)





Logical operators (and, nand, nor, not, or, xor, xnor, mand, mor)





Control structures (if-then-else, ternary conditional, switch case, return-statement)





Loop structures (while loop, for loop, repeat until loop, break, continue)





Optimization of expressions (constant folding, strength reduction, operator coupling, special functions and dead code elimination)





String operations (equalities, inequalities, logical operators, concatenation and sub-ranges)





Expression local variables, vectors and strings





User defined variables, vectors, strings, constants and function support





Multivariate function composition





Multiple sequence point and sub expression support





Numeric integration and differentiation





Vector Processing: BLAS-L1 (axpy, axpby, axpb), all/any-true/false, count, rotate-left/right, shift-left/right, sort, nth_element, iota, sum, kahan-sum, dot-product, copy





(axpy, axpby, axpb), all/any-true/false, count, rotate-left/right, shift-left/right, sort, nth_element, iota, sum, kahan-sum, dot-product, copy File-IO package (routines include: open, close, read, write, getline, support for binary and text modes)





Support for various numeric types (float, double, long double, MPFR/GMP )





) 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 MIT License.

Compatibility

The C++ Mathematical Expression Library implementation is compatible with the following C++ compilers:

GNU Compiler Collection (3.5+)

Intel® C++ Compiler (8.x+)

Clang/LLVM (1.1+)

PGI C++ (10.x+)

Microsoft Visual Studio C++ Compiler (7.1+)

IBM XL C/C++ (9.x+)

C++ Builder (XE4+)

Download

ExprTk REPL (ReadEvaluatePrint Loop) Binaries: Double Type: Linux - Windows MPFR Type: Linux - Windows

Basic Design

When compiling and subsequently evaluating an expression with ExprTk, the following three fundamental components will be encountered:

Component Purpose/Details exprtk::symbol_table<NumericType> Holder of external variables, vectors, strings, constants and user defined functions exprtk::parser<NumericType> Expression factory exprtk::expression<NumericType> Holder of the AST which is used to evaluate the compiled expression

Note: NumericType can be any floating point type. This includes but is not limited to: float, double, long double, MPFR or any custom type conforming to an interface comptaible with the standard floating point type.

The following diagram depicts each of the above denoted components and how they relate to one another when compiling and evaluating the expression: z := x - (3 * y)

The following example illustrates, using C++ code, the happenings of the above mentioned diagram. Given an expression string 'z := x - (3 * y)' and three variables (x, y and z), a exprtk::symbol_table is instanitated and the variables are added to it. Then an exprtk::expression is instantiated and the symbol table is registered with the expression instance. Finally a exprtk::parser is instantiated where both the expression object and the string form of the expression are passed to a method of the parser called compile.

If the compilation process is successful the expression instance will now be holding an AST that can further be used to evaluate the original expression. Otherwise a compilation error will be raised and diagnostics relating to the error(s) made available via the parser's error reporting interface. The expression in the example will perform a calculation using the variables x and y then proceed to assign the result of the calculation to the variable z.

typedef double T; // numeric type (float, double, mpfr etc...) typedef exprtk::symbol_table<T> symbol_table_t; typedef exprtk::expression<T> expression_t; typedef exprtk::parser<T> parser_t; std::string expression_string = "z := x - (3 * y)"; T x = T(123.456); T y = T(98.98); T z = T(0.0); symbol_table_t symbol_table; symbol_table.add_variable("x",x); symbol_table.add_variable("y",y); symbol_table.add_variable("z",z); expression_t expression; expression.register_symbol_table(symbol_table); parser_t parser; if (!parser.compile(expression_string,expression)) { printf("Compilation error...

"); return; } T result = expression.value(); ....

The expression is evaluated by traversing the generated AST in postorder manner. The order of sub-expression evaluations will be as follows:

Result0 z

For a more detailed discussion on the internals of ExprTk and its capabilities it is recommended to have a review of the accompanying: readme.txt

Example Expressions

sqrt(1 - (x^2))

clamp(-1,sin(2 * pi * x) + cos(y / 2 * pi),+1)

sin(2 * x)

if (((x + 2) == 3) and ((y + 5) <= 9),1 + w, 2 / z)

inrange(-2,m,+2) == (({-2 <= m} and [m <= +2]) ? 1 : 0)

({1 / 1} * [1 / 2] + (1 / 3)) - {1 / 4} ^ [1 / 5] + (1 / 6) -({1 / 7} + [1 / 8]*(1 / 9))

a * exp(2 * t) + c

z := x + sin(2 * pi / y)

2x + 3y + 4z + 5w == 2 * x + 3 * y + 4 * z + 5 * w

3(x + y) / 2 + 1 == 3 * (x + y) / 2 + 1

(x + y)3 + 1 / 4 == (x + y) * 3 + 1 / 4

(x + y)z + 1 / 2 == (x + y) * z + 1 / 2

(sin(x/pi)cos(2y) + 1)==(sin(x / pi) * cos(2 * y) + 1)

while(x <= 100) { x += 1; }

x <= 'abc123' and (y in ('AStr' + 'ing')) or ('1x2y3z' != z)

('REX' + x like '*123*') or ('a123b' ilike y)

Simple ExprTk Examples

Renaissance Technologies - Rentec

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. [src: simple_example_01.cpp]

template <typename T> void trig_function() { typedef exprtk::symbol_table<T> symbol_table_t; typedef exprtk::expression<T> expression_t; typedef exprtk::parser<T> parser_t; std::string expression_string = "clamp(-1.0,sin(2 * pi * x) + cos(x / 2 * pi),+1.0)"; T x; symbol_table_t symbol_table; symbol_table.add_variable("x",x); symbol_table.add_constants(); expression_t expression; expression.register_symbol_table(symbol_table); parser_t parser; parser.compile(expression_string,expression); for (x = T(-5); x <= T(+5); x += T(0.001)) { T y = expression.value(); printf("%19.15f\t%19.15f

",x,y); } }

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. [src: simple_example_02.cpp]

template <typename T> void square_wave() { typedef exprtk::symbol_table<T> symbol_table_t; typedef exprtk::expression<T> expression_t; typedef exprtk::parser<T> parser_t; 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.141592653589793238462643383279502); const T f = pi / T(10); const T a = T(10); T t = T(0); symbol_table_t symbol_table; symbol_table.add_variable("t",t); symbol_table.add_constant("f",f); symbol_table.add_constant("a",a); symbol_table.add_constants(); expression_t expression; expression.register_symbol_table(symbol_table); parser_t parser; parser.compile(expr_string,expression); const T delta = T(4) * pi / T(1000); for (t = T(-2) * pi; t <= T(+2) * pi; t += delta) { T result = expression.value(); printf("%19.15f\t%19.15f

",t,result); } }

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 '*' [src: simple_example_03.cpp]

template <typename T> void polynomial() { typedef exprtk::symbol_table<T> symbol_table_t; typedef exprtk::expression<T> expression_t; typedef exprtk::parser<T> parser_t; std::string expression_string = "25x^5 - 35x^4 - 15x^3 + 40x^2 - 15x + 1"; T r0 = T(0); T r1 = T(1); T x = T(0); symbol_table_t symbol_table; symbol_table.add_variable("x",x); expression_t expression; expression.register_symbol_table(symbol_table); 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

",x,expression.value()); } }

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 composited functions with expression local variables. [src: 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(x) compositor .add("fibonacci", " var w := 0; " " var y := 0; " " var z := 1; " " switch " " { " " case x == 0 : 0; " " case x == 1 : 1; " " default : " " while ((x -= 1) > 0) " " { " " w := z; " " z := z + y; " " y := w; " " z " " }; " " } ", "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

",i,result); } }

The following example demonstrates how one can easily register a custom user defined function to be used within expression evaluations. In this example one of the custom functions myfunc takes two parameters as input and returns a result, the other being a free function named myotherfunc which takes three values as input and returns a result. Furthermore the 'myfunc' function explicitly enables itself for constant-folding optimisations by indicating it is stateless and has no external side-effects. The upper limit for individual parameters is 20 inputs. If more inputs are required then either the ivararg_function or igeneric_function interfaces can be used, both of which support an unlimited number of input parameters. [src: simple_example_05.cpp]

template <typename T> struct myfunc : public exprtk::ifunction<T> { myfunc() : exprtk::ifunction<T>(2) { exprtk::disable_has_side_effects(*this); } inline T operator()(const T& v1, const T& v2) { return T(1) + (v1 * v2) / T(3); } }; template <typename T> inline T myotherfunc(T v0, T v1, T v2) { return std::abs(v0 - v1) * v2; } template <typename T> void custom_function() { typedef exprtk::symbol_table<T> symbol_table_t; typedef exprtk::expression<T> expression_t; typedef exprtk::parser<T> parser_t; std::string expression_string = "myfunc(sin(x / pi), otherfunc(3 * y, x / 2, x * y))"; T x = T(1); T y = T(2); myfunc<T> mf; 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_function("otherfunc",myotherfunc); symbol_table.add_constants(); expression_t expression; expression.register_symbol_table(symbol_table); parser_t parser; parser.compile(expression_string,expression); T result = expression.value(); printf("Result: %10.5f

",result); }

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. The example demonstrates the use of vector indexing, the vector 'size' operator and for loops. [src: simple_example_06.cpp]

template <typename T> void vector_function() { typedef exprtk::symbol_table<T> symbol_table_t; typedef exprtk::expression<T> expression_t; typedef exprtk::parser<T> parser_t; std::string expression_string = " for (var i := 0; i < min(x[],y[],z[]); i += 1) " " { " " z[i] := 3sin(x[i]) + 2log(y[i]); " " } "; T x[] = { T(1.1), T(2.2), T(3.3), T(4.4), T(5.5) }; T y[] = { T(1.1), T(2.2), T(3.3), T(4.4), T(5.5) }; T z[] = { T(0.0), T(0.0), T(0.0), T(0.0), T(0.0) }; symbol_table_t symbol_table; symbol_table.add_vector("x",x); symbol_table.add_vector("y",y); symbol_table.add_vector("z",z); expression_t expression; expression.register_symbol_table(symbol_table); parser_t parser; parser.compile(expression_string,expression); expression.value(); }

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. [src: simple_example_07.cpp]

template <typename T> void logic() { typedef exprtk::symbol_table<T> symbol_table_t; typedef exprtk::expression<T> expression_t; typedef exprtk::parser<T> parser_t; std::string expression_string = "not(A and B) or C"; 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); parser_t parser; parser.compile(expression_string,expression); printf(" # | A | B | C | %s

" "---+---+---+---+-%s

", 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

", 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

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. [src: 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 err_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

", parser.error().c_str(), expression_string.c_str()); for (std::size_t i = 0; i < parser.error_count(); ++i) { err_t error = parser.get_error(i); printf("Error: %02d Position: %02d Type: [%14s] Msg: %s\tExpression: %s

", 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

", expression_string.c_str(), result); }

The following example demonstrates the computation of prime numbers via a mixture of recursive composited functions, switch-statement and while-loop functionalities. [src: 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); 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); 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

", i, (result1 == T(1)) ? 'T' : 'F', (result2 == T(1)) ? 'T' : 'F', (result3 == T(1)) ? 'T' : 'F'); } }

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, expression local variables and the repeat until loop. [src: 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); symbol_table_t symbol_table; symbol_table.add_constants(); symbol_table.add_variable("x",x); compositor_t compositor(symbol_table); compositor .add("newton_sqrt", "switch " "{ " " case x < 0 : null; " " case x == 0 : 0; " " case x == 1 : 1; " " default: " " ~{ " " var z := 100; " " var sqrt_x := x / 2; " " repeat " " if (equal(sqrt_x^2, x)) " " break[sqrt_x]; " " else " " sqrt_x := (1 / 2) * (sqrt_x + (x / sqrt_x)); " " until ((z -= 1) <= 0) " " }; " "} ", "x"); std::string expression_str = "newton_sqrt(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 < 100; ++i) { x = i; T result = expression.value(); printf("sqrt(%03d) - Result: %12.10f\tReal: %12.10f

", static_cast<unsigned int>(i), result, std::sqrt(x)); } }

The following is similar to Example 2. However in this example, the square wave form is generated using 1000 haramonics. The example demonstrates the use of for-loops, implied multiplications, expression local variables and addition/multiplication assignment operators. [src: simple_example_11.cpp]

template <typename T> void square_wave() { typedef exprtk::symbol_table<T> symbol_table_t; typedef exprtk::expression<T> expression_t; typedef exprtk::parser<T> parser_t; std::string wave_program = " var r := 0; " " for (var i := 0; i < 1000; i += 1) " " { " " r += (1 / (2i + 1)) * sin((4i + 2) * pi * f * t); " " }; " " r *= a * (4 / pi); "; static const T pi = T(3.141592653589793238462643383279502); T f = pi / T(10); T t = T(0); T a = T(10); 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(); expression_t expression; expression.register_symbol_table(symbol_table); parser_t parser; parser.compile(wave_program,expression); const T delta = T(4) * pi / T(1000); for (t = (T(-2) * pi); t <= (T(+2) * pi); t += delta) { T result = expression.value(); printf("%19.15f\t%19.15f

",t,result); } }

The following is an example of the 'venerable' Bubble Sort algorithm. The example demonstrates functionality such as vector elements, nested for-loops, repeat-until loop, expression local variables and the swap operator. [src: simple_example_12.cpp]

template <typename T> void bubble_sort() { typedef exprtk::symbol_table<T> symbol_table_t; typedef exprtk::expression<T> expression_t; typedef exprtk::parser<T> parser_t; std::string bubblesort_program = " var upper_bound := v[]; " " var swapped := false; " " repeat " " swapped := false; " " for (var i := 0; i < upper_bound; i += 1) " " { " " for (var j := i + 1; j < upper_bound; j += 1)" " { " " if (v[i] > v[j]) " " { " " v[i] <=> v[j]; " " swapped := true; " " }; " " }; " " }; " " upper_bound -= 1; " " until (not(swapped) or (upper_bound == 0)); "; T v[] = { T(9.9), T(2.2), T(1.1), T(5.5), T(7.7), T(4.4), T(3.3) }; symbol_table_t symbol_table; symbol_table.add_vector("v",v); expression_t expression; expression.register_symbol_table(symbol_table); parser_t parser; parser.compile(bubblesort_program,expression); expression.value(); }

The following is an example implementation of the Savitzky-Golay smoothing filter. A periodic signal with Additive White noise is generated (v_in), the Savitzky-Golay filter is then applied to the noisy signal and output to the vector v_out. The graph below denotes the noisy and smoothed signals in blue and red respectively. The example demonstrates the use of user defined vectors, expression local vectors and variables, nested for-loops, conditional statements and the vector size operator. [src: simple_example_13.cpp]

template <typename T> void savitzky_golay_filter() { typedef exprtk::symbol_table<T> symbol_table_t; typedef exprtk::expression<T> expression_t; typedef exprtk::parser<T> parser_t; std::string sgfilter_program = " var weight[9] := " " { " " -21, 14, 39, " " 54, 59, 54, " " 39, 14, -21 " " }; " " " " if (v_in[] >= weight[]) " " { " " var lower_bound := trunc(weight[] / 2); " " var upper_bound := v_in[] - lower_bound; " " " " v_out := 0; " " " " for (var i := lower_bound; i < upper_bound; i += 1) " " { " " for (var j := -lower_bound; j <= lower_bound; j += 1) " " { " " v_out[i] += weight[j + lower_bound] * v_in[i + j]; " " }; " " }; " " " " v_out /= sum(weight); " " } "; const std::size_t n = 1024; std::vector<T> v_in; std::vector<T> v_out; const T pi = T(3.141592653589793238462643383279502); srand(time(0)); // Generate a signal with noise. for (T t = T(-5); t <= T(+5); t += T(10.0 / n)) { T noise = T(0.5 * (rand() / (RAND_MAX + 1.0) - 0.5)); v_in.push_back(std::sin(2.0 * pi * t) + noise); } v_out.resize(v_in.size()); symbol_table_t symbol_table; symbol_table.add_vector("v_in" , v_in); symbol_table.add_vector("v_out",v_out); expression_t expression; expression.register_symbol_table(symbol_table); parser_t parser; parser.compile(sgfilter_program,expression); expression.value(); for (std::size_t i = 0; i < v_out.size(); ++i) { printf("%10.6f\t%10.6f

",v_in[i],v_out[i]); } }

The following example calculates the Standard Deviation of a vector x comprised of values in the range [1,25]. The example demonstrates the vector capabilities of ExprTk, such as the definition and initialisation of expression local vectors, unary-operator functions over vectors, scalar-vector arithmetic and the vector size operator. [src: simple_example_14.cpp]

template <typename T> void stddev_example() { typedef exprtk::expression<T> expression_t; typedef exprtk::parser<T> parser_t; std::string stddev_program = " var x[25] := { " " 1, 2, 3, 4, 5, " " 6, 7, 8, 9, 10, " " 11, 12, 13, 14, 15, " " 16, 17, 18, 19, 20, " " 21, 22, 23, 24, 25 " " }; " " " " sqrt(sum([x - avg(x)]^2) / x[]) "; expression_t expression; parser_t parser; parser.compile(stddev_program,expression); T stddev = expression.value(); printf("stddev(1..25) = %10.6f

",stddev); }

The following example calculates the call and put prices of a European option using the Black-Scholes-Merton pricing model. The example demonstrates the use of user defined and expression local variables, conditional statements, string comparisons and basic arithmetic functionality. [src: simple_example_15.cpp]

template <typename T> void black_scholes_merton_model() { typedef exprtk::symbol_table<T> symbol_table_t; typedef exprtk::expression<T> expression_t; typedef exprtk::parser<T> parser_t; std::string bsm_model_program = " var d1 := (log(s / x) + (r + v^2 / 2) * t) / (v * sqrt(t)); " " var d2 := d1 - v * sqrt(t); " " " " if (callput_flag == 'call') " " s * ncdf(d1) - x * e^(-r * t) * ncdf(d2); " " else if (callput_flag == 'put') " " x * e^(-r * t) * ncdf(-d2) - s * ncdf(-d1); " " "; T s = T(60.00); // Stock price T x = T(65.00); // Strike price T t = T( 0.25); // Years to maturity T r = T( 0.08); // Risk free rate T v = T( 0.30); // Volatility std::string callput_flag; static const T e = exprtk::details::numeric::constant::e; symbol_table_t symbol_table; symbol_table.add_variable("s",s); symbol_table.add_variable("x",x); symbol_table.add_variable("t",t); symbol_table.add_variable("r",r); symbol_table.add_variable("v",v); symbol_table.add_constant("e",e); symbol_table.add_stringvar("callput_flag",callput_flag); expression_t expression; expression.register_symbol_table(symbol_table); parser_t parser; parser.compile(bsm_model_program,expression); { callput_flag = "call"; T bsm = expression.value(); printf("BSM(%s,%5.3f,%5.3f,%5.3f,%5.3f,%5.3f) = %10.6f

", callput_flag.c_str(), s, x, t, r, v, bsm); } { callput_flag = "put"; T bsm = expression.value(); printf("BSM(%s,%5.3f,%5.3f,%5.3f,%5.3f,%5.3f) = %10.6f

", callput_flag.c_str(), s, x, t, r, v, bsm); } }

The following example attempts to compute a linear fit for a set of 2D data points utilizing the Linear Least Squares method. The data points are comprised of two vectors named x and y plotted as blue on the chart, the result being a linear equation in the form of y = β x + α which is depicted in red. The example demonstrates the use of user defined vectors, vector operations and aggregators and conditional statements. [src: simple_example_16.cpp]

template <typename T> void linear_least_squares() { typedef exprtk::symbol_table<T> symbol_table_t; typedef exprtk::expression<T> expression_t; typedef exprtk::parser<T> parser_t; std::string linear_least_squares_program = " if (x[] == y[]) " " { " " beta := (sum(x * y) - sum(x) * sum(y) / x[]) / " " (sum(x^2) - sum(x)^2 / x[]); " " " " alpha := avg(y) - beta * avg(x); " " " " rmse := sqrt(sum((beta * x + alpha - y)^2) / y[]); " " } " " else " " { " " alpha := null; " " beta := null; " " rmse := null; " " } "; T x[] = {T( 1), T( 2), T(3), T( 4), T( 5), T(6), T( 7), T( 8), T( 9), T(10)}; T y[] = {T(8.7), T(6.8), T(6), T(5.6), T(3.8), T(3), T(2.4), T(1.7), T(0.4), T(-1)}; T alpha = T(0); T beta = T(0); T rmse = T(0); symbol_table_t symbol_table; symbol_table.add_variable("alpha",alpha); symbol_table.add_variable("beta" ,beta ); symbol_table.add_variable("rmse" ,rmse ); symbol_table.add_vector("x",x); symbol_table.add_vector("y",y); expression_t expression; expression.register_symbol_table(symbol_table); parser_t parser; parser.compile(linear_least_squares_program,expression); expression.value(); printf("alpha: %15.12f

",alpha); printf("beta: %15.12f

",beta ); printf("rmse: %15.12f

",rmse ); printf("y = %15.12fx + %15.12f

",beta,alpha); }

The following example will compute an approximate value for π using the Monte-Carlo method. The example demonstrates the use of vector inequality operations, vector initialisation via expressions, the summation aggregator and user defined functions for generating a uniformly distributed random value in the range [0,1). [src: simple_example_17.cpp]

template <typename T> struct rnd_01 : public exprtk::ifunction<T> { rnd_01() : exprtk::ifunction<T>(0) { ::srand(static_cast<unsigned int>(time(NULL))); } inline T operator()() { return T(::rand() / T(RAND_MAX + 1)); } }; template <typename T> void monte_carlo_pi() { typedef exprtk::symbol_table<T> symbol_table_t; typedef exprtk::expression<T> expression_t; typedef exprtk::parser<T> parser_t; std::string monte_carlo_pi_program = " var experiments[5 * 10^7] := [(rnd_01^2 + rnd_01^2) <= 1]; " " 4 * sum(experiments) / experiments[]; "; rnd_01<T> rnd01; symbol_table_t symbol_table; symbol_table.add_function("rnd_01",rnd01); expression_t expression; expression.register_symbol_table(symbol_table); parser_t parser; parser.compile(monte_carlo_pi_program,expression); const T approximate_pi = expression.value(); const T real_pi = T(3.141592653589793238462643383279502); printf("pi ~ %20.17f\terror: %20.17f

", approximate_pi, std::abs(real_pi - approximate_pi)); }

The following example will open a file named 'file.txt' in write mode, and write to the file a piece of text ten times, then finally close the file. The following example demonstrates the Exprtk file I/O package capabilities, handle manipulations, the null type, local string variables, if-else statements and for-loops. [src: simple_example_18.cpp]

template <typename T> void file_io() { typedef exprtk::symbol_table<T> symbol_table_t; typedef exprtk::expression<T> expression_t; typedef exprtk::parser<T> parser_t; std::string fileio_program = " var file_name := 'file.txt'; " " var stream := null; " " " " if (stream := open(file_name,'w')) " " println('Successfully opened file: ' + file_name); " " else " " { " " println('Failed to open file: ' + file_name); " " return [false]; " " } " " " " var s := 'Hello world...

'; " " " " for (var i := 0; i < 10; i += 1) " " { " " write(stream,s); " " } " " " " if (close(stream)) " " println('Sucessfully closed file: ' + file_name); " " else " " { " " println('Failed to close file: ' + file_name); " " return [false]; " " } "; exprtk::rtl::io::file::package<T> fileio_package; exprtk::rtl::io::println<T> println; symbol_table_t symbol_table; symbol_table.add_function("println",println); symbol_table.add_package (fileio_package ); expression_t expression; expression.register_symbol_table(symbol_table); parser_t parser; parser.compile(fileio_program,expression); printf("Result %10.3f

",expression.value()); }

The following example illustrates a var-arg igeneric_function definition that can handle two specific overloads. The first being a vector only, the second being a vector and range (using two scalars) input set. The function when invoked will populate the input vector over the defined range with uniformly distributed pseudo-random values in the range [0,1). The example expression takes a signal vector, then proceeds to add a noise vector to the signal, resulting in a 'noisy-signal'. Furthermore the resulting noisy signal's average as well as its element-wise form is printed to stdout using a for-loop. [src: simple_example_19.cpp]

template <typename T> class randu : public exprtk::igeneric_function<T> { public: typedef typename exprtk::igeneric_function<T> igfun_t; typedef typename igfun_t::parameter_list_t parameter_list_t; typedef typename igfun_t::generic_type generic_type; typedef typename generic_type::vector_view vector_t; using exprtk::igeneric_function<T>::operator(); randu() : exprtk::igeneric_function<T>("V|VTT") /* Overloads: 0. V - vector 1. VTT - vector, r0, r1 */ { ::srand(static_cast<unsigned int>(time(NULL))); } inline T operator()(const std::size_t& ps_index, parameter_list_t parameters) { vector_t v(parameters[0]); std::size_t r0 = 0; std::size_t r1 = v.size() - 1; if ( (1 == ps_index) && !exprtk::rtl::vecops::helper:: load_vector_range<T>::process(parameters, r0, r1, 1, 2, 0) ) return T(0); for (std::size_t i = r0; i <= r1; ++i) { v[i] = rnd(); } return T(1); } private: inline T rnd() { // Note: Do not use this in production // Result is in the interval [0,1) return T(::rand() / T(RAND_MAX + 1.0)); } }; template <typename T> void vector_randu() { typedef exprtk::symbol_table<T> symbol_table_t; typedef exprtk::expression<T> expression_t; typedef exprtk::parser<T> parser_t; std::string vecrandu_program = " var noise[6] := [0]; " " " " if (randu(noise,0,5) == false) " " { " " println('Failed to generate noise'); " " return [false]; " " } " " " " var noisy[6] := signal + (noise - 1/2); " " " " for (var i := 0; i < noisy[]; i += 1) " " { " " println('noisy[',i,'] = ', noisy[i]); " " } " " " " println('avg: ', avg(noisy)); " " "; T signal[] = { T(1.1), T(2.2), T(3.3), T(4.4), T(5.5), T(6.6), T(7.7) }; exprtk::rtl::io::println<T> println; randu<T> randu; symbol_table_t symbol_table; symbol_table.add_vector ("signal" , signal); symbol_table.add_function("println",println); symbol_table.add_function("randu" , randu); expression_t expression; expression.register_symbol_table(symbol_table); parser_t parser; parser.compile(vecrandu_program,expression); expression.value(); }

Renaissance Technologies - Rentec

The chart below depicts the rate of expression evaluations per second for an assortment of expressions. 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 7.2 with O3, LTO, PGO and native architecture target compiler settings, and executed upon an Intel Xeon E5-2687W 3GHz CPU, 64GB RAM, Ubuntu 17.10 with kernel 4.10 system.

(y + x)

(2 * y + 2 * x)

((1.23 * x^2) / y) - 123.123

(y + x / y) * (x - y / x)

x / ((x + y) + (x - y)) / y

1 - ((x * y) + (y / x)) - 3

1.1x^1 + 2.2y^2 - 3.3x^3 + 4.4y^15 - 5.5x^23 + 6.6y^55

sin(2 * x) + cos(pi / y)

1 - sin(2 * x) + cos(pi / y)

sqrt(111.111 - sin(2 * x) + cos(pi / y) / 333.333)

(x^2 / sin(2 * pi / y)) -x / 2

x + (cos(y - sin(2 / x * pi)) - sin(x - cos(2 * y / pi))) - y

clamp(-1.0, sin(2 * pi * x) + cos(y / 2 * pi), +1.0)

max(3.33, min(sqrt(1 - sin(2 * x) + cos(pi / y) / 3), 1.11))

if (avg(x,y) <= x + y, x - y, x * y) + 2 * pi / x

The following is a benchmark based on Example 15. The BSM pricing model is executed using native and ExprTk based implementations. There are two ExprTk implementations, vanilla and another where e(...) is replaced with the equivalent and much faster exp(...) function and also repeated sub-calculations are cached locally. The benchmark depicts the number of pricing calculations per second. The benchmark itself was compiled using GCC 7.2 with O3, LTO, PGO and native architecture target compiler settings, and executed upon an Intel Xeon E5-2687W 3GHz CPU, 64GB RAM, Ubuntu 17.10 with kernel 4.10 system. exprtk_bsm_benchmark.cpp

The following is a brief listing of some of the users of the ExprTk library:

Renaissance Technologies - Rentec