libSBML Perl API  libSBML 5.8.0 Perl API
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Working with mathematical expressions

This section describes libSBML's facilities for working with SBML representations of mathematical expressions.

Basic concepts

LibSBML uses Abstract Syntax Trees (ASTs) to provide a canonical, in-memory representation for all mathematical formulas regardless of their original format (i.e., C-like infix strings or MathML 2.0). In libSBML, an AST is a collection of one or more objects of class ASTNode. An AST node in libSBML is a recursive structure containing a pointer to the node's value (which might be, for example, a number or a symbol) and a list of children nodes. Each ASTNode node may have none, one, two, or more child depending on its type. The following diagram illustrates an example of how the mathematical expression "1 + 2" is represented as an AST with one plus node having two integer children nodes for the numbers 1 and 2. The figure also shows the corresponding MathML 2.0 "content" representation:

Example AST representation of a mathematical expression.
Infix AST MathML
1 + 2 <math xmlns="http://www.w3.org/1998/Math/MathML">
  <apply>
    <plus/>
    <cn type="integer"> 1 </cn>
    <cn type="integer"> 2 </cn>
  </apply>
</math>

The following are noteworthy about the AST representation in libSBML:

  • A numerical value represented in MathML as a real number with an exponent is preserved as such in the AST node representation, even if the number could be stored in a double data type. This is done so that when an SBML model is read in and then written out again, the amount of change introduced by libSBML to the SBML during the round-trip activity is minimized.
  • Rational numbers are represented in an AST node using separate numerator and denominator values. These can be retrieved using the methods getNumerator() and getDenominator() on an ASTNode object.
  • The children of an ASTNode are other ASTNode objects. The list of children is empty for nodes that are leaf elements, such as numbers. For nodes that are actually roots of expression subtrees, the list of children points to the parsed objects that make up the rest of the expression.

For many applications, the details of ASTs are irrelevant because the applications can use the text-string based translation functions such as SBML_formulaToString(), SBML_parseL3Formula() and SBML_parseFormula(). If you find the complexity of using the AST representation of expressions too high for your purposes, perhaps the string-based functions will be more suitable.

Finally, it is worth noting that the AST and MathML handling code in libSBML remains written in C, not C++. (All of libSBML was originally written in C.) Readers may occasionally wonder why some aspects are more C-like and less object oriented, and that's one of the reasons.

Converting between ASTs and text strings

SBML Levels 2 and 3 represent mathematical expressions using MathML, but most applications do not use MathML directly. Instead, applications generally either interact with mathematics in text-string form, or else they use the API for working with Abstract Syntax Trees (described below). LibSBML provides support for both approaches. The libSBML formula parser has been carefully engineered so that transformations from MathML to infix string notation and back is possible with a minimum of disruption to the structure of the mathematical expression.

The example below shows a simple program that, when run, takes a MathML string compiled into the program, converts it to an AST, converts that to an infix representation of the formula, compares it to the expected form of that formula, and finally translates that formula back to MathML and displays it. The output displayed on the terminal should have the same structure as the MathML it started with. The program is a simple example of using the various MathML and AST reading and writing methods, and shows that libSBML preserves the ordering and structure of the mathematical expressions.

#include <iostream>
#include <sbml/SBMLTypes.h>

int
main (int argc, char *argv[])
{
  const char* expected = "1 + f(x)";

  const char* s = "<?xml version='1.0' encoding='UTF-8'?>"
    "<math xmlns='http://www.w3.org/1998/Math/MathML'>"
    "  <apply> <plus/> <cn> 1 </cn>"
    "                  <apply> <ci> f </ci> <ci> x </ci> </apply>"
    "  </apply>"
    "</math>";

  ASTNode* ast    = readMathMLFromString(s);
  char*    result = SBML_formulaToString(ast);

  if ( strcmp(result, expected) == 0 )
    cout << "Got expected result" << endl;
  else
    cout << "Mismatch after readMathMLFromString()" << endl;

  ASTNode* new_mathml = SBML_parseFormula(result);
  char*    new_s      = writeMathMLToString(new_mathml);

  cout << "Result of writing AST:" << endl << new_s << endl;
}

The text-string form of mathematical formulas produced by SBML_formulaToString() and read by SBML_parseFormula() and SBML_parseL3Formula() are in a simple C-inspired infix notation. It is summarized in the next section below. A formula in this text-string form therefore can be handed to a program that understands SBML mathematical expressions, or used as part of a translation system. The libSBML distribution comes with an example program in the "examples" subdirectory called translateMath that implements an interactive command-line demonstration of translating infix formulas into MathML and vice-versa. In summary, the functions available are the following:

The string formula syntax and differences with MathML

The text-string formula syntax is an infix notation essentially derived from the syntax of the C programming language and was originally used in SBML Level 1. The formula strings may contain operators, function calls, symbols, and white space characters. The allowable white space characters are tab and space. The following are illustrative examples of formulas expressed in the syntax:

0.10 * k4^2
(vm * s1)/(km + s1)

The following table shows the precedence rules in this syntax. In the Class column, operand implies the construct is an operand, prefix implies the operation is applied to the following arguments, unary implies there is one argument, and binary implies there are two arguments. The values in the Precedence column show how the order of different types of operation are determined. For example, the expression a * b + c is evaluated as (a * b) + c because the * operator has higher precedence. The Associates column shows how the order of similar precedence operations is determined; for example, a - b + c is evaluated as (a - b) + c because the + and - operators are left-associative. The precedence and associativity rules are taken from the C programming language, except for the symbol ^, which is used in C for a different purpose. (Exponentiation can be invoked using either ^ or the function power.)

Token Operation Class Precedence Associates
namesymbol referenceoperand6n/a
(expression)expression groupingoperand6n/a
f(...)function callprefix6left
-negationunary5right
^powerbinary4left
*multiplicationbinary3left
/divisonbinary3left
+additionbinary2left
-subtractionbinary2left
,argument delimiterbinary1left
A table of the expression operators and their precedence in the text-string format for mathematical expressions used by SBML_parseFormula().

A program parsing a formula in an SBML model should assume that names appearing in the formula are the identifiers of Species, Parameter, Compartment, FunctionDefinition, (in Level 2) Reaction, or (in Level 3) SpeciesReference objects defined in a model. When a function call is involved, the syntax consists of a function identifier, followed by optional white space, followed by an opening parenthesis, followed by a sequence of zero or more arguments separated by commas (with each comma optionally preceded and/or followed by zero or more white space characters), followed by a closing parenthesis. There is an almost one-to-one mapping between the list of predefined functions available, and those defined in MathML. All of the MathML functions are recognized; this set is larger than the functions defined in SBML Level 1. In the subset of functions that overlap between MathML and SBML Level 1, there exist a few differences. The following table summarizes the differences between the predefined functions in SBML Level 1 and the MathML equivalents in SBML Levels 2 and 3:

Text string formula functions MathML equivalents in SBML Levels 2 and 3
acosarccos
asinarcsin
atanarctan
ceilceiling
logln
log10(x)log(10, x)
pow(x, y)power(x, y)
sqr(x)power(x, 2)
sqrt(x)root(2, x)
Table comparing the names of certain functions in the SBML text-string formula syntax and MathML. The left column shows the names of functions recognized by SBML_parseFormula(); the right column shows their equivalent function names in MathML 2.0, used in SBML Levels 2 and 3.

Methods for working with libSBML's Abstract Syntax Trees

Every ASTNode in a libSBML abstract syntax tree has an associated type, which is a value taken from the enumeration ASTNodeType_t. The list of possible types is quite long, because it covers all the mathematical functions that are permitted in SBML. The values are shown in the following table; their names hopefully evoke the construct that they represent:

  • AST_CONSTANT_E
  • AST_CONSTANT_FALSE
  • AST_CONSTANT_PI
  • AST_CONSTANT_TRUE
  • AST_DIVIDE
  • AST_FUNCTION
  • AST_FUNCTION_ABS
  • AST_FUNCTION_ARCCOS
  • AST_FUNCTION_ARCCOSH
  • AST_FUNCTION_ARCCOT
  • AST_FUNCTION_ARCCOTH
  • AST_FUNCTION_ARCCSC
  • AST_FUNCTION_ARCCSCH
  • AST_FUNCTION_ARCSEC
  • AST_FUNCTION_ARCSECH
  • AST_FUNCTION_ARCSIN
  • AST_FUNCTION_ARCSINH
  • AST_FUNCTION_ARCTAN
  • AST_FUNCTION_ARCTANH
  • AST_FUNCTION_CEILING
  • AST_FUNCTION_COS
  • AST_FUNCTION_COSH
  • AST_FUNCTION_COT
  • AST_FUNCTION_COTH
  • AST_FUNCTION_CSC
  • AST_FUNCTION_CSCH
  • AST_FUNCTION_DELAY
  • AST_FUNCTION_EXP
  • AST_FUNCTION_FACTORIAL
  • AST_FUNCTION_FLOOR
  • AST_FUNCTION_LN
  • AST_FUNCTION_LOG
  • AST_FUNCTION_PIECEWISE
  • AST_FUNCTION_POWER
  • AST_FUNCTION_ROOT
  • AST_FUNCTION_SEC
  • AST_FUNCTION_SECH
  • AST_FUNCTION_SIN
  • AST_FUNCTION_SINH
  • AST_FUNCTION_TAN
  • AST_FUNCTION_TANH
  • AST_INTEGER
  • AST_LAMBDA
  • AST_LOGICAL_AND
  • AST_LOGICAL_NOT
  • AST_LOGICAL_OR
  • AST_LOGICAL_XOR
  • AST_MINUS
  • AST_NAME
  • AST_NAME_AVOGADRO (Level 3 only)
  • AST_NAME_TIME
  • AST_PLUS
  • AST_POWER
  • AST_RATIONAL
  • AST_REAL
  • AST_REAL_E
  • AST_RELATIONAL_EQ
  • AST_RELATIONAL_GEQ
  • AST_RELATIONAL_GT
  • AST_RELATIONAL_LEQ
  • AST_RELATIONAL_LT
  • AST_RELATIONAL_NEQ
  • AST_TIMES
  • AST_UNKNOWN

There are a number of methods for interrogating the type of an ASTNode and for testing whether a node belongs to a general category of constructs. The methods on ASTNode for this purpose are the following:

  • ASTNodeType_t getType() returns the type of this AST node.
  • bool isConstant() returns true if this AST node is a MathML constant (true, false, pi, exponentiale), false otherwise.
  • bool isBoolean() returns true if this AST node returns a boolean value (by being either a logical operator, a relational operator, or the constant true or false).
  • bool isFunction() returns true if this AST node is a function (i.e., a MathML defined function such as exp or else a function defined by a FunctionDefinition in the Model).
  • bool isInfinity() returns true if this AST node is the special IEEE 754 value infinity.
  • bool isInteger() returns true if this AST node is holding an integer value.
  • bool isNumber() returns true if this AST node is holding any number.
  • bool isLambda() returns true if this AST node is a MathML lambda construct.
  • bool isLog10() returns true if this AST node represents the log10 function, specifically, that its type is AST_FUNCTION_LOG and it has two children, the first of which is an integer equal to 10.
  • bool isLogical() returns true if this AST node is a logical operator (and, or, not, xor).
  • bool isName() returns true if this AST node is a user-defined name or (in SBML Levels 2 and 3) one of the two special csymbol constructs "delay" or "time".
  • bool isNaN() returns true if this AST node has the special IEEE 754 value "not a number" (NaN).
  • bool isNegInfinity() returns true if this AST node has the special IEEE 754 value of negative infinity.
  • bool isOperator() returns true if this AST node is an operator (e.g., +, -, etc.)
  • bool isPiecewise() returns true if this AST node is the MathML piecewise function.
  • bool isRational() returns true if this AST node is a rational number having a numerator and a denominator.
  • bool isReal() returns true if this AST node is a real number (specifically, AST_REAL_E or AST_RATIONAL).
  • bool isRelational() returns true if this AST node is a relational operator.
  • bool isSqrt() returns true if this AST node is the square-root operator
  • bool isUMinus() returns true if this AST node is a unary minus.
  • bool isUnknown() returns true if this AST node's type is unknown.

Programs manipulating AST node structures should check the type of a given node before calling methods that return a value from the node. The following are the ASTNode object methods available for returning values from nodes:

Of course, all of this would be of little use if libSBML didn't also provide methods for setting the values of AST node objects! And it does. The methods are the following:

  • void setCharacter(char value) sets the value of this ASTNode to the given character value. If character is one of +, -, *, / or ^, the node type will be to the appropriate operator type. For all other characters, the node type will be set to AST_UNKNOWN.
  • void setName(const char name) sets the value of this AST node to the given name. The node type will be set (to AST_NAME) only if the AST node was previously an operator (isOperator(node) != 0) or number (isNumber(node) != 0). This allows names to be set for AST_FUNCTIONs and the like.
  • void setValue(int value) sets the value of the node to the given integer value. Equivalent to the next method.
  • void setValue(long value) sets the value of the node to the given integer value. Equivalent to the previous method. No, this is not a Gödelian self-referential loop.
  • void setValue(long numerator, long denominator) sets the value of this ASTNode to the given rational value in two parts: the numerator and denominator. The node type is set to AST_RATIONAL.
  • void setValue(double value) sets the value of this ASTNode to the given real (double) value and sets the node type to AST_REAL.
  • void setValue(double mantissa, long exponent) sets the value of this ASTNode to a real (double) using the two parts given: the mantissa and the exponent. The node type is set to AST_REAL_E.

Finally, ASTNode also defines some miscellaneous methods for manipulating ASTs:

Reading and Writing Mathematical Expressions into ASTs

As mentioned above, applications often can avoid working with raw MathML by using either libSBML's text-string interface or the AST API. However, when needed, reading MathML content directly and creating ASTs is easily done in libSBML using a method designed for this purpose:

Similarly, writing out Abstract Syntax Tree structures is easily done using the following method:

The example program given above demonstrate the use of these methods.