Cexprtk
cexprtk is a cython wrapper around "C++ Mathematical Expression Toolkit Library (ExprTk)". Using cexprtk a powerful mathematical expression engine can be incorporated into your python project.
Install / Use
/learn @mjdrushton/CexprtkREADME
cexprtk: Mathematical Expression Parsing and Evaluation in Python
cexprtk is a cython wrapper around the "[ExprTK: C++ Mathematical Expression Toolkit Library ][ExprTK]" by Arash Partow. Using cexprtk a powerful mathematical expression engine can be incorporated into your python project.
Table of Contents
- Installation
- Usage
- API Reference
- Authors
- License
Installation
The latest version of cexprtk can be installed using [pip][pip] :
$ pip install cexprtk
Note: Installation requires a compatible C++ compiler to be installed (unless installing from a binary wheel).
Usage
The following examples show the major features of cexprtk.
Example: Evaluate a simple equation
The following shows how the arithmetic expression (5+5) * 23 can be evaluated:
>>> import cexprtk
>>> cexprtk.evaluate_expression("(5+5) * 23", {})
230.0
Example: Using Variables
Variables can be used within expressions by passing a dictionary to the evaluate_expression function. This maps variable names to their values. The expression from the previous example can be re-calculated using variable values:
>>> import cexprtk
>>> cexprtk.evaluate_expression("(A+B) * C", {"A" : 5, "B" : 5, "C" : 23})
230.0
Example: Re-using expressions
When using the evaluate_expression() function, the mathematical expression is parsed, evaluated and then immediately thrown away. This example shows how to re-use an Expression for multiple evaluations.
- An expression will be defined to calculate the circumference of circle, this will then be re-used to calculate the value for several different radii.
- First a
Symbol_Tableis created containing a variabler(for radius), it is also populated with some useful constants such as π.
>>> import cexprtk
>>> st = cexprtk.Symbol_Table({'r' : 1.0}, add_constants= True)
- Now an instance of
Expressionis created, defining our function:
>>> circumference = cexprtk.Expression('2*pi*r', st)
- The
Symbol_Tablewas initialised withr=1, the expression can be evaluated for this radius simply by calling it:
>>> circumference()
6.283185307179586
- Now update the radius to a value of 3.0 using the dictionary like object returned by the
Symbol_Table's.variablesproperty:
>>> st.variables['r'] = 3.0
>>> circumference()
18.84955592153876
Example: Defining custom functions
Python functions can be registered with a Symbol_Table then used in an Expression. In this example a custom function will be defined which produces a random number within a given range.
A suitable function exists in the random module, namely random.uniform. As this is an instance method it needs to be wrapped in function:
>>> import random
>>> def rnd(low, high):
... return random.uniform(low,high)
...
Our rnd function now needs to be registered with a Symbol_Table:
>>> import cexprtk
>>> st = cexprtk.Symbol_Table({})
>>> st.functions["rand"] = rnd
The functions property of the Symbol_Table is accessed like a dictionary. In the preceding code snippet, a symbol table is created and then the rnd function is assigned to the rand key. This key is used as the function's name in a cexprtk expression. The key cannot be the same as an existing variable, constant or reserved function name.
The rand function will now be used in an expression. This expression chooses a random number between 5 and 8 and then multiplies it by 10. The followin snippet shows the instantiation of the Expression which is then evaluated a few times. You will probably get different numbers out of your expression than shown, this is because your random number generator will have been initialised with a different seed than used in the example.
>>> e = cexprtk.Expression("rand(5,8) * 10", st)
>>> e()
61.4668441077191
>>> e()
77.13523163246415
>>> e()
59.14881842716157
>>> e()
69.1476535568958
Example: Defining an unknown symbol resolver
A callback can be passed to the Expression constructor through the unknown_symbol_resolver_callback parameter. This callback is invoked during expression parsing when a variable or constant is encountered that isn't in the Symbol_Table associated with the Expression.
The callback can be used to provide some logic that leads to a new symbol being registered or for an error condition to be flagged.
The Problem: The following example shows a potential use for the symbol resolver:
- An expression contains variables of the form
m_VARIABLENAMEandf_VARIABLENAME. m_orf_prefix the actual variable name (perhaps indicating gender).VARIABLENAMEshould be used to look up the desired value in a dictionary.- The dictionary value of
VARIABLENAMEshould then be weighted according to its prefix:m_variables should be multiplied by 0.8.f_variables should be multiplied by 1.1.
The Solution:
-
First the
VARIABLENAMEdictionary is defined:variable_values = { 'county_a' : 82, 'county_b' : 76} -
Now the callback is defined. This takes a single argument, symbol, which gives the name of the missing variable found in the expression:
def callback(symbol): # Tokenize the symbol name into prefix and VARIABLENAME components. prefix,variablename = symbol.split("_", 1) # Get the value for this VARIABLENAME from the variable_values dict value = variable_values[variablename] # Find the correct weight for the prefix if prefix == 'm': weight = 0.8 elif prefix == 'f': weight = 1.1 else: # Flag an error condition if prefix not found. errormsg = "Unknown prefix "+ str(prefix) return (False, cexprtk.USRSymbolType.VARIABLE, 0.0, errormsg) # Apply the weight to the value *= weight # Indicate success and return value to cexprtk return (True, cexprtk.USRSymbolType.VARIABLE, value, "") -
All that remains is to register the callback with an instance of
Expressionand to evaluate an expression. The expression to be evaluated is:(m_county_a - f_county_b)- This should give a value of
(0.8*82) - (1.1*76) = -18
>>> st = cexprtk.Symbol_Table({}) >>> e = cexprtk.Expression("(m_county_a - f_county_b)", st, callback) >>> e.value() -18.0
Example: expressions that contain return statements to produce multiple values
Exprtk expressions can return multiple values the results these expressions can be accessed through the results() method.
The following example shows the result of adding a constant value to a vector containing numbers:
>>> st = cexprtk.Symbol_Table({})
>>> e = cexprtk.Expression("var v[3] := {1,2,3}; return [v+1];", st)
>>> e.value()
nan
>>> e.results()
[[2.0, 3.0, 4.0]]
Note that expression has to be evaluated before calling the results() method.
The value accessed through results() can contain a mixture of strings, vectors and real values:
>>> st = cexprtk.Symbol_Table({'c' : 3})
>>> e = cexprtk.Expression("if(c>1){return ['bigger than one', c];} else { return ['not bigger than one',c];};",st)
>>> e.value()
nan
>>> e.results()
['bigger than one', 3.0]
>>> st.variables['c']=0.5
>>> e.value()
nan
>>> e.results()
['not bigger than one', 0.5]
API Reference
For information about expressions supported by cexprtk please refer to the original C++ [ExprTK][] documentation:
Class Reference
class Expression:
Class representing mathematical expression.
- Following instantiation, the expression is evaluated calling the expression or invoking its
value()method. - The variable values used by the Expression can be modified through the
variablesproperty of theSymbol_Tableinstance associated with the expression. TheSymbol_Tablecan be accessed using theExpression.symbol_tableproperty.
Defining unknown symbol-resolver:
The unknown_symbol_resolver_callback argument to the Expression
constructor accepts a callable which is invoked whenever a symbol (i.e. a
variable or a constant), is not found in the `Sym
