'Unexpected behaviour when parsing string with sympy

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


Solution 1:[1]

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)

Solution 2:[2]

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).

Solution 3:[3]

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)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 smichr
Solution 2 asmeurer
Solution 3 cards