Advertisement

muparser - Fast Math Parser Library

Version 2.2.5


Evaluating an expression

Single return value

Expression evaluation is done by calling the mupEval() function in the DLL version or the Eval() member function of a parser object. When evaluating an expression for the first time the parser evaluates the expression string directly and creates a bytecode during this first time evaluation. Every sucessive call to Eval() will evaluate the bytecode directly unless you call a function that will silently reset the parser to string parse mode. Some functions invalidate the bytecode due to possible changes in callback function pointers or variable addresses. By doing so they effectively cause a recreation of the bytecode during the next call to Eval().

Internally there are different evaluation functions. One for parsing from a string, the other for parsing from bytecode (and a third one used only if the expression can be simplified to a constant). Initially, Eval() will call the string parsing function which is slow due to all the necessary syntax checking, variable lookup, and bytecode creation. Once this function succeeds, Eval() will change its internal parse function pointer to either the bytecode parsing function or the const result function which are significantly (approx. 1000 times) faster. You don't have to worry about this, it's done automatically, just keep in mind that the first time evaluation of an expression is significantly slower than any successive call to Eval().

[DLL interface]

double fVal;
fVal = mupEval(hParser);
See also: example2/example2.c.

[Parser class interface]

double fVal;
try
{
  fVal = parser.Eval();
}
catch (Parser::exception_type &e)
{
  std::cout << e.GetMsg() << endl;
}
See also: example1/example1.cpp.

If the expression contains multiple separated subexpressions the return value of Eval()/ mupEval() is the result of the last subexpression. If you need all of the results use the Eval overload described in the next section.

Multiple return values

muParser accepts expressions that are made up of several subexpressions delimited by the function argument separator. For instance take a look at the following expression:

sin(x),y+x,x*x

It is made up of three expression separated by commas hence it will create three return values. (Assuming the comma is defined as the argument separator). The number of return values as well as their value can be queried with an overload of the Eval function. This overload takes a reference to an integer value for storing the total number of return values and returns a pointer to an array of value_type holding the actual values with the first value at the botton of the array and the last at the top.

[DLL interface]

  int nNum, i;
  muFloat_t *v = mupEvalMulti(hParser, &nNum);
  for (i=0; i<nNum; ++i)
  {
    printf("v[i]=%2.2f\n", v[i]);
  }

[Parser class interface]

  int nNum;
  value_type *v = parser.Eval(nNum);
  for (int i=0; i<nNum; ++i)
  {
    std::cout << v[i] << "\n";
  }
See also: example1/example1.cpp.

The function GetNumResults() can be used in order to finf out whether a given expression has produced multiple return values.

Bulk mode evaluations

The basic idea behind the bulkmode is to minimize the overhead of function calls and loops when using muParser inside of large loops. Each loop turn requires a distinct set of variables and setting these variables followed by calling the evaluation function can by slow if the loop is implemented in a managed language. This overhead can be minimized by precalculating the variable values and calling just a single evaluation function. In reality the bulkmode doesn't make much of a difference when used in C++ but it brings a significant increase in performance when used in .NET applications. If muParser was compiled with OpenMP support the calculation load will be spread among all available CPU cores. When using the bulk mode variable pointers submitted to the DefineVar function must be arrays instead of single variables. All variable arrays must have the same size and each array index represents a distinct set of variables to be used in the expression.

Although the bulk mode does work with standard callback functions it may sometimes be necessary to have additional informations inside a callback function. Especially Informations like the index of the current variable set and the index of the thread performing the calculation may be crucial to the evaluation process. To facilitate this need a special set of callback functions was added.

[DLL interface]

void CalcBulk()
{
  int nBulkSize = 200, i;

  // allocate the arrays for variables and return values
  muFloat_t *x = (muFloat_t*)malloc(nBulkSize * sizeof(muFloat_t));
  muFloat_t *y = (muFloat_t*)malloc(nBulkSize * sizeof(muFloat_t));
  muFloat_t *r = (muFloat_t*)malloc(nBulkSize * sizeof(muFloat_t));

  // initialize the parser and variables
  muParserHandle_t hParser = mupCreate(muBASETYPE_FLOAT);  
  for (i=0; i<nBulkSize; ++i)
  {
    x[i] = i;
    y[i] = i;
    r[i] = 0;
  }

  // Set up variables and functions and evaluate the expression
  mupDefineVar(hParser, "x", x);  
  mupDefineVar(hParser, "y", y);
  mupDefineBulkFun1(hParser, "bulktest", BulkTest);
  mupSetExpr(hParser, "bulktest(x+y)");
  mupEvalBulk(hParser, r, nBulkSize);
  if (mupError(hParser))
  {
    printf("\nError:\n");
    printf("------\n");
    printf("Message:  %s\n", mupGetErrorMsg(hParser) );
    printf("Token:    %s\n", mupGetErrorToken(hParser) );
    printf("Position: %d\n", mupGetErrorPos(hParser) );
    printf("Errc:     %d\n", mupGetErrorCode(hParser) );
    return;
  }

  // Output the result
  for (i=0; i<nBulkSize; ++i)
  {
    printf("%d: bulkfun(%2.2f + %2.2f) = %2.2f\n", i, x[i], y[i], r[i]);
  }

  free(x);
  free(y);
  free(r);
}

You might also like: