C++ Mathematical Expression Library

 www.partow.net  .: Home :.   .: Links :.   .: Search :.   .: Contact :. 


Description

The C++ Mathematical Expression Toolkit Library (ExprTk) is a simple to use, easy to integrate and extremely efficient run-time mathematical expression parsing and evaluation engine. ExprTk supports numerous forms of functional and logic processing semantics and is 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, 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 (:=, +=, -=, *=, /=, %=)
  • Boolean logic (and, mand, mor, nand, nor, not, or, xor, xnor)
  • Control Structures (if-then-else, ternary conditional, switch case)
  • Loop Structures (while loop, for loop, repeat until loop, break, continue)
  • Optimization of expressions (constant folding, strength reduction, operator coupling and special functions)
  • String operations (equalities, inequalities, boolean logic, concatenation and ranges)
  • Multiple and custom variable support
  • Expression local variables and vectors
  • User defined variables, vectors, strings, constants and function support
  • Multivariate function composition
  • Multiple sequence point and sub expression support
  • Numeric integration and differentiation
  • 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 most current version of the "Common Public License."

Compatability

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

  • GNU Compiler Collection (3.3+)
  • Intel® C++ Compiler (8.x+)
  • Clang/LLVM (1.1+)
  • PGI C++ (10.x+)
  • Microsoft Visual Studio C++ Compiler (7.1+)
  • Comeau C++ Compiler (4.3+)
  • IBM XL C/C++ (9.x+)
  • C++ Builder (XE4+)

Download

The C++ Mathematical Expression Library Download - Copyright Arash Partow C++ Mathematical Expression Library Source Code and Examples

The C++ Mathematical Expression Library Download - Copyright Arash Partow ExprTk MSVC Solutions (2010, 2013)

The C++ Mathematical Expression Library Download - Copyright Arash Partow ExprTk MPFR Adaptor


Example Expressions

The C++ Mathematical Expression Library Example - Copyright Arash Partow

  • 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 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); x <= T(+5); x += T(0.001))
   {
      T y = expression.value();
      printf("%19.15f\t%19.15f\n",x,y);
   }
}
                     

The C++ Mathematical Expression Library Graph - Copyright Arash Partow


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 / T(10);
   T t = T(0);
   T a = T(10);

   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 = 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\n",t,result);
   }
}
                     

The C++ Mathematical Expression Library Sqaure Wave Graph - Copyright Arash Partow


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);
   T r1 = T(1);
   T  x = T(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());
   }
}
                       

The C++ Mathematical Expression Library Polynomial Graph - Copyright Arash Partow


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 composited functions with expression local variables. 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\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);
   T y = T(2);

   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. The example demonstrates the use of vector indexing, the vector 'size' operator and for loops. 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);

   exprtk::parser_t parser;

   parser.compile(expression_string,expression);

   expression.value();
}
                       

Simple Example 7

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);

   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\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, expression local variables 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);

   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  : -inf;                "
           "  case x == 0 : 0;                   "
           "  case x == 1 : 1;                   "
           "  default:                           "
           "  ~{                                 "
           "     var z := 100;                   "
           "     var y := x / 2;                 "
           "     repeat                          "
           "       y := (1 / 2) * (y + (x / y)); "
           "       if (equal(y * y,x))           "
           "         break[y];                   "
           "     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\n",
             static_cast<unsigned int>(i),
             result,
             std::sqrt(x));
   }
}                       

Simple Example 11

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. simple_example_11.cpp

template <typename T>
void square_wave()
{
   std::string expr_string =
                  " 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.14159265358979323846);

   T f = pi / T(10);
   T t = T(0);
   T a = T(10);

   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 = 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\n",t,result);
   }
}
                     

The C++ Mathematical Expression Library Sqaure Wave II Graph - Copyright Arash Partow


Simple Example 12

The following is an example of the venerable Bubble Sort algorithm. The example demonstrates the use of operations using vector elements and nested for-loops, expression local variables and the swap operator. 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(2.2), T(1.1), T(5.5), 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();
}
                     

Simple Example 13

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. 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);                                  "
                  "                                                          "
                  "   for (var i := 0; i < lower_bound; i += 1)              "
                  "   {                                                      "
                  "     v_out[i] := 0;                                       "
                  "     v_out[v_out[] - i - 1] := 0;                         "
                  "   };                                                     "
                  " }                                                        ";

   const std::size_t n = 1024;

   std::vector<T> v_in;
   std::vector<T> v_out;

   const T pi = T(3.141592653589793238462);

   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\n",v_in[i],v_out[i]);
   }
}
                     

The C++ Mathematical Expression Library Savitzky Golay Filter - Copyright Arash Partow


Simple Example 14

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. 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\n",stddev);
}
                     

Simple Example 15

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. 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\n",
             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\n",
             callput_flag.c_str(),
             s,x,t,r,v,
             bsm);
   }
}
                     

Simple Example 16

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. 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\n",alpha);
   printf("beta:  %15.12f\n",beta );
   printf("rmse:  %15.12f\n",rmse );
   printf("y = %15.12fx + %15.12f\n",beta,alpha);
}
                     

The C++ Mathematical Expression Library Linear Least Squares - Copyright Arash Partow


Various Examples


Benchmarks

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 4.9 with O3, PGO and native architecture target compiler settings, and executed upon a 64-Bit Intel Quad Core Extreme i7-920XM 2.0GHz, 32GB RAM, Ubuntu 10.10 kernel 2.6.35 system.

  • (y + x)
  • 2 * (y + 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 C++ Mathematical Expression Library Benchmark Result - Copyright Arash Partow


Black-Scholes-Merton Pricing Model Benchmark

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 repeated sub-calculations are cached locally. The benchmark depicts the number of pricing calculations per second. The benchmark was compiled using GCC 4.9, with O2, PGO and LTO and executed upon a 64-Bit Intel Quad Core Extreme i7-920XM 2.0GHz with 32GB RAM. exprtk_bsm_benchmark.cpp

The C++ Mathematical Expression Library Black Scholes Merton - Copyright Arash Partow





Copyright Arash Partow