Unexpected behaviour when parsing string with sympy - python

I'm trying to perform the derivative of an equation with sympy, but, while if I write the equation by hand the derivative is correct; when I pass the equation as a string, the output is wrong. Can anyone explain me how to solve this issue? I'm using python 3.6 and sympy 1.5.1.
>>>from sympy import *
>>>from operator import *
>>> x1 = symbols('x1')
>>> f = add(sin(x1), mul(x1, x1))
>>> diff(f, x1)
2*x1 + cos(x1) ## Correct output
>>>> f = 'add(sin(x1), mul(x1, x1))' ## Equation provided as string
>>>> diff(f, x1)
(Subs(Derivative(mul(_xi_1, x1), _xi_1), _xi_1, x1) + Subs(Derivative(mul(x1, _xi_2), _xi_2), _xi_2, x1))*Subs(Derivative(add(sin(x1), _xi_2), _xi_2), _xi_2, mul(x1, x1)) + cos(x1)*Subs(Derivative(add(_xi_1, mul(x1, x1)), _xi_1), _xi_1, sin(x1)) ## Wrong output

This is happening because f = 'add(sin(x1), mul(x1, x1))' is not a valid mathematical equation that can be parsed by parse_expr. This function is designed to parse equations written in mathematical syntax, not in terms of Sympy functions. To get this function in particular to be parsed correctly, you would need to use, for example:
>>> f = 'sin(x1) + x1^2'
>>> diff(f, x1)
2*x1 + cos(x1)
If you really need to use that specific string, you could use eval():
>>> f = 'add(sin(x1), mul(x1, x1))'
>>> diff(eval(f), x1)
2*x1 + cos(x1)

If you want to write it in this fashion, be sure to use the actual SymPy object names (which are capitalized). I use S(...) to interpret the expression and that is the same thing that any function would do, too:
>>> S('Add(sin(x1), Mul(x1, x1))')
x1**2 + sin(x1)
But you can also use the mathematical operators + and *:
>>> S('sin(x1) + x1*x1')
x1**2 + sin(x1)

You shouldn't pass strings directly to SymPy functions. Rather, first parse them with sympify (which is the same as S). You can pass a dictionary of names as the second argument to sympify if you want non-standard names like add to map to existing SymPy ones, like
sympify('add(x, y)', {'add': Add}) # Gives x + y
Otherwise sympify will assume that any unknown functions are undefined functions like f(x).

Sympy provides parser functions to turn string into sympy object, see doc parse_expr.
For custom functions translation you can make a mapping with those provide by sympy and pass it as argument, local_dict, to the parser.
from sympy import Add, Mul, sin
from sympy.parsing.sympy_parser import parse_expr
f = parse_expr('add(sin(x1), mul(x1, x1))', local_dict={'add': Add, 'mul': Mul})
f.diff('x1')
Output
2x1 + cos(x1)

Related

How to save a function as string in Python?

Given the following math function in form of a Python function:
import math
def f(x):
a = x - math.log(x)
b = x + math.log(x)
return a / x + b / math.log(x)
Is there any way that I can convert this function into a string like
expr = '(x - math.log(x)) / x + (x + math.log(x)) / math.log(x)'
so that when I want to call the function, I can simply use it by
func = lambda x: eval(expr)
print(func(3))
# 4.364513583657809
Note that I want to keep a and b in the original function. In reality, I have a lot more intermediate variables. Also, I am aware sympy could do similar tasks, but I would like to know if it is possible to convert the function to string, as it would be much more efficient to store.
Any suggestions?
Your function is already a string the moment you write it to a file!
If the function is valid Python, you can then just import it
from myfile import expr
print(expr(3)) # 4.364513583657809
WARNING Do not ever do this
If you want some incredibly evil logic for some reason, you can save your function directly with inspect.getsource(f) and then do something like this
>>> fn_body = """def f(x):
... a = x - math.log(x)
... b = x + math.log(x)
... return a / x + b / math.log(x)
... """
>>> eval(f'lambda {fn_body.split("(")[1].split(")")[0]}, _={exec(fn_body)}: {fn_body.split(" ", 1)[-1].split(")")[0]})')(3)
4.364513583657809
This works by finding the parts needed to call the function, evaluating the source as one of the args (to smuggle it into your namespace), and then building an anonymous function to call it
Further Caveats
not remotely maintainable
extremely fragile
will clobber or conflict with an existing function with the same name depending on use
you will still need to import math or whatever other libraries
won't work with default args without more pain
calling eval() first (before creating the lambda) will allow you to use inspect to get the signature (.signature()) and you can combine it with re and/or ast for a much robust parser, but a 1-liner seemed more exciting
manages to use both eval() and exec() for an extra helping of evil
You're probably looking for a symbolic equation solver!
Sympy's lambdify feature can do this for you!
>>> fn = sympy.lambdify("x", '(x - log(x)) / x + (x + log(x)) / log(x)')
>>> fn(x=3)
4.364513583657809
Caution: this also uses eval internally as #Joshua Voskamp warns about in a comment

Vector definition through sympify method in Sympy

I use Sympy in my Python project, were the string definition of the expression converted to Sympy through sympify method. For example:
import sympy as sp
exp1 = sp.sympify('Add(Rational(1, 5), pi)')
While I am able to describe almost all functionality, I ran into the problem of Vector description to be understood by sympify method. Vector definition in normal way:
from sympy.vector import CoordSys3D
R = CoordSys3D('R')
v = 3*R.i + 4*R.j + 5*R.k
Next example (one of a variety) is failing:
exp2 = sp.sympify('Vector(Coord3D().i*3, Coord3D().j*4, Coord3D().k*5')
I would like to keep Vector format to support the curl, divergence and gradient functionality. Definition of 1-row matrix does not suit here for me. Documentation research did not give results.
I would be grateful for an example or suggestion of the right way for the sympy.vector definition for the sympify method.
If you pass in a locals dictionary, will that do what you want:
>>> from sympy.vector import *
>>> sympify('Vector(R.i*3, R.j*4, R.k*5)',
... locals=dict(R=CoordSys3D('R'), Vector=Vector))
Vector(3*R.i, 4*R.j, 5*R.k)
>>> type(_)
<class 'sympy.vector.vector.Vector'>

sympy parser doesn't identify expm1 as a symbolic function

The function expm1 is not parsed properly in the following example:
from sympy.parsing.sympy_parser import parse_expr
print parse_expr('expm1(x)').diff('x')
gives
Derivative(expm1(x), x)
How can I get sympy identifying expm1 as symbolic function, so that I get the same result as
print parse_expr('exp(x) - 1').diff('x')
which gives exp(x)?
Since there is no built-in expm1 in SymPy, the parser does not know anything about this notation. The parameter local_dict of parse_expr can be used to explain the meaning of unfamiliar functions and symbols to SymPy.
expm1 = lambda x: exp(x)-1
parse_expr('expm1(x)', local_dict={"expm1": expm1})
This returns exp(x) - 1.
For expm1 to remain a single function with known derivative, rather than exp(x)-1, define it as a SymPy function (see tutorial for more such examples).
class expm1(Function):
def fdiff(self, argindex=1):
return exp(self.args[0])
A confirmation that this works:
e = parse_expr('expm1(x)', local_dict={"expm1": expm1})
print(e) # expm1(x)
print(e.diff(x)) # exp(x)
f = lambdify(x, e)
print(f(1)) # 1.718281828459045
print(f(1e-20)) # 1e-20, unlike exp(x)-1 which would evaluate to 0

Derivative of a parsed SymPy expression is always 0

I am writing a program that requires the user to enter an expression. This expression is entered as a string and converted to a Sympy expression using parse_expr. I then need to take the partial derivative of that expression that the user entered. However, diff is returning 0 with every expression I am testing.
For example if the user enters a*exp(-b*(x-c)**(2)), using the following code, diff returns 0 when it should (as far as I know about diff) return 2*a*b*(c - x)*exp(-b*(x - c)**2) when taking the partial derivative with respect to x:
a, b, c, x = symbols('a b c x', real=True)
str_expr = "a*exp(-b*(x-c)**(2))"
parsed_expr = parse_expr(str_expr)
result = diff(parsed_expr, x)
print(result) # prints 0
What am I doing wrong?
Bottom line: use parse_expr(str_expr,locals()).
Add global_dict=<dict of allowed entities to use>, too, if the expression may use any entities not imported into the local namespace and not accessible with the default from sympy import *.
According to Calculus — SymPy Tutorial - SymPy 1.0.1.dev documentation, you type the symbolic expression into the diff() argument as-is. Due to the fact that the letters are Symbol objects (with overridden operators), Python is tricked into constructing the SymPy object corresponding to the expression as it evaluates the argument!
Thus, if you have it as a string, you eval it to trigger the same behaviour:
<...>
>>> s="a*exp(-b*(x-c)**(2))"
>>> diff(eval(s), x)
−ab(−2c+2x)e−b(−c+x)2
But eval is a security hazard if used with untrusted input because it accepts arbitrary Python code.
This is where replacements like parse_expr come into play. However, due to the way expressions are parsed, described above, it needs access to the external entities used in the expression - like the Symbol objects for variables and function objects for the named functions used - through the local_dict and global_dict arguments.
Otherwise, it creates the Symbol objects on the fly. Which means, the Symbol object it has created for x in the expression is different from the variable x! No wonder that the derivative over it is 0!
<...>
>>> ps=parse_expr(s)
>>> ps.free_symbols
{a,b,c,x}
>>> x in _
False
>>> diff(ps,x)
0
>>> ps=parse_expr(s,locals())
>>> x in ps.free_symbols
True
>>> diff(ps,x)
-ab(−2c+2x)e−b(−c+x)2
Work is ongoing to make sympify safer than eval. Better to use something like the following:
from sympy import *
var ('a b c x')
str_expr = "a*exp(-b*(x-c)**(2))"
parsed_expr = sympify(str_expr)
result = diff(parsed_expr, x)
print(result)
Result:
-a*b*(-2*c + 2*x)*exp(-b*(-c + x)**2)
Replace a, b, c, x = symbols('a b c x', real=True) with:
a = Symbol('a')
b = Symbol('b')
c = Symbol('c')
x = Symbol('x')
Symbols with different assumptions compare unequal:
>>> Symbol('x') == Symbol('x', real=True)
False
When you use sympify or parse_expr, it parses unknown variables as symbols without assumptions. In your case, this creates Symbol('x'), which is considered distinct from the Symbol('x', real=True) you already created.
The solution is to either remove the assumptions, or include the locals() dictionary when you parse, so that it recognizes the name x as being the Symbol('x', real=True) that you already defined, like
parse_expr(str_expr,locals())
or
sympify(str_expr, locals())

Python (SymPy, SciPy), create symbol lambda from string

I've struggling to take text inputs of an equation and evaluate it as a definite integral. I need a callable function to pass to scipy.integrate.
eq = "x**2"
func = lambda x: eq
func(2)
# outputs:
# x**2
# but if I:
func = lambda x: x**2
func(2)
# outputs:
# 4
Not sure, but maybe you are looking for
eq = "x**2"
func = eval("lambda x: " + eq)
Note that using eval() is dangerous if eq is from an untrusted source (e.g. user input).
You need to use eval to run eq as code and not treat it as a string.
eq = "x**2"
func = lambda x: eval(eq)
func(2)
# outputs:
# 4
sympify may be what you are looking for. It converts a string expression into an sympy object.
For example:
import sympy as sp
f=sp.sympify('x**2+sin(y)')
And you can use autowrap to convert a sympy object into a callable function.
Try asteval or numexpr for possibly safer alternatives to eval() and Sympy.sympify().evalf().
I continually got a syntax error for the following and relentlessly was trying to find out why:
E="2x+1"
F=lambda x: (x, eval(E)) # to get an xy coordinate
But the issue worked as expected when I changed E to:
E="2*x+1"
Rookie mistake. :)

Categories