Skip to content

bertiqwerty/mexpress

Repository files navigation

Mexpress

Math parser and evaluator in Python capable of computing gradients and Hessians. Mexpress uses the Rust crate Exmex.

Installation

PyPI version example workflow dependency status

pip install mexpress

Usage

import mexpress
import numpy as np

# parse function with parse_f64 or parse_f32
f = mexpress.parse_f64("(x - 1) ** 2 - y * x + 3 * y ** 2")

# evaluate function at (2, 4)
y = f(2.0, 4.0)
assert abs(y - 41) < 1e-12

# evaluate gradient at (2, 4)
grad_2_4 = f.grad(2.0, 4.0)
assert np.linalg.norm(grad_2_4 - [-2, 22]) < 1e-12

# evaluate Hessian at (2, 4)
hess_2_4 = f.hess(2.0, 4.0)
assert np.linalg.norm(hess_2_4 - [[2, -1], [-1, 6]]) < 1e-12

Besides ** you can also use ^ for exponentiation. Currently, a list of supported mathematical operators can be found in the documentation of Exmex.

Optimization Example

With gradients and Hessians one can at least locally optimize differentiable functions passed as strings, e.g., with scipy.optimize.

from scipy.optimize import minimize
import mexpress

func_str = f"(1 - x) ** 2 + 100 * (y - x ** 2) ** 2 + (z - 7) ** 2 + (ρ + 5) ** 2"
f = mexpress.parse_f64(func_str)
res = minimize(f, x0=[0.0, 0.0, 0.0, 0.0], method="trust-ncg", jac=f.grad, hess=f.hess)

We have played around with different methods to optimize func_str, see the following output of py/demo/opt.py. In the following table, #fails is the number of fails out of 100 attempts with random x0s. The iterations and elapsed seconds are medians.

CG             #fails   0   #it  44   0.0049996 sec   jac True    hess False
CG             #fails  23   #it  44   0.0179558 sec   jac False   hess False
Newton-CG      #fails   0   #it  38   0.0049839 sec   jac True    hess True
Newton-CG      #fails   5   #it  37   0.0059988 sec   jac True    hess False
trust-krylov   #fails   0   #it  31   0.0255845 sec   jac True    hess True
trust-ncg      #fails   0   #it  32   0.0030000 sec   jac True    hess True
trust-exact    #fails   0   #it  30   0.0060000 sec   jac True    hess True
BFGS           #fails   0   #it  72   0.0059998 sec   jac True    hess False
BFGS           #fails  21   #it  74   0.0169995 sec   jac False   hess False
L-BFGS-B       #fails   0   #it  43   0.0019979 sec   jac True    hess False
L-BFGS-B       #fails   0   #it  42   0.0069985 sec   jac False   hess False
Nelder-Mead    #fails   0   #it 441   0.0131288 sec   jac False   hess False
SLSQP          #fails   0   #it  34   0.0029492 sec   jac True    hess False
dogleg         #fails  17   #it  27   0.0027690 sec   jac True    hess True
TNC            #fails   0   #it  29   0.0029995 sec   jac True    hess False
TNC            #fails   0   #it  27   0.0110002 sec   jac False   hess False
COBYLA         #fails  46   #it  -1   0.0163412 sec   jac False   hess False
POWELL         #fails   0   #it  22   0.0139999 sec   jac False   hess False