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
Related
How to solve the multi object problem?
import sympy as sym
from sympy import lambdify
x = sym.Symbol('x')
n = sym.Symbol('n')
f = sym.sin(n*x)
derivative_f = f.diff(x)
derivative_f = lambdify(x, derivative_f)
x = float(input('x:'))
print(derivative_f(x))
print(derivative_f)
If I input 2, the expected result should be 2*cos(2*x).
Your code contains a few misconceptions. One problem is an important general programming rule: try to use different variable names for variables with different meanings. So, x shouldn't be assigned a float, as it was a symbolic variable before. And derivative_f being a symbolic expression, shouldn't be assigned the result of lambdify.
Note that sympy's symbolic world doesn't mix well with the numeric world of non-sympy functions. lambdify forms a bridge between these worlds, from completely symbolic to completely numeric. E.g. the function created with lambdify doesn't have access to the symbolic n anymore.
The code lambdify(x, derivative_f) contains an error. derivative_f is a symbolic expression containing two symbolic variables (x and n), so it needs to be called as derivative_f_x_n = lambdify((x, n), derivative_f) (also giving the result a different name). Afterwards, you can use numeric expressions as derivative_f_x_n(7, 8), but you can't use symbolic parameters anymore.
For what you seem to be trying to do, lambdify isn't adequate. To get the derivative with x substituted, you call .subs(x, new_value) directly on the symbolic version of derivative_f:
import sympy as sym
from sympy import lambdify
x = sym.Symbol('x')
n = sym.Symbol('n')
f = sym.sin(n * x)
derivative_f_x = f.diff(x)
x_float = 2.0
print(derivative_f_x.subs(x, x_float))
Output: n*cos(2.0*n)
Also note that sympy strongly prefers to work with exact symbolic expressions, and using floats inevitably brings in approximations. Whenever possible, integers, sympy fractions (sym.S(1)/2) or symbolic expressions (sym.sqrt(5)) are recommended.
You call also use the derivative with respect to x and then substitute n:
print(f.diff(x).subs(n, 2))
Output: 2*cos(2*x)
To use that function later on in numeric calculations, after substitution you only have one symbolic variable left(x):
g = lambdify(x, f.diff(x).subs(n, 2))
You can type help(g) to see its generated source code:
Source code:
def _lambdifygenerated(x):
return (2*cos(2*x))
Then you can use g e.g. to create matplotlib plot. After lambdify nothing is symbolic anymore.
import matplotlib.pyplot as plt
import numpy as np
xs = np.linspace(0, 10)
plt.plot(xs, g(xs))
The other answer hit the key point, that you needed to account for the symbolic n when lambdifying.
Look at the help of your function:
In [2]: help(derivative_f)
Help on function _lambdifygenerated:
_lambdifygenerated(x)
Created with lambdify. Signature:
....
Source code:
def _lambdifygenerated(x):
return (n*cos(n*x))
It's a function of x, drawing n from the global environment, here a symbol.
The full error message when given an array argument:
In [5]: derivative_f(np.array([.1,.2,.3]))
AttributeError: 'Mul' object has no attribute 'cos'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<ipython-input-5-e04e9f3c0a8e>", line 1, in <module>
derivative_f(np.array([.1,.2,.3]))
File "<lambdifygenerated-1>", line 2, in _lambdifygenerated
return (n*cos(n*x))
TypeError: loop of ufunc does not support argument 0 of type Mul which has no callable cos method
See the return line in the traceback. That's the 'source code' in the help, and tells you what numpy is trying to execute.
test the inner expression of that line:
In [6]: n*np.array([.1,.2,.3])
Out[6]: array([0.1*n, 0.2*n, 0.3*n], dtype=object)
This is an object dtype array. Math on such an array delegates the calculation to each of the elements. Specifically it tries
(0.1*n).cos()
0.1*n is a sympy mul expression, hence the error. symbols often don't work in numpy expressions.
lambdify is a handy way of creating numpy functions from sympy expressions, but it needs to be used with care, and full awareness of what it does, and does not, do.
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)
Suppose you have computed fu as a result of a sympy calculation:
fu= sy.cos(x)+sy.sin(y)+1
where
x,y = sy.symbols("x y")
are symbols. Now you want to turn fu to a numpy function of (obviously) two variables.
You can do this by:
fun= sy.lambdify((x,y), fu, "numpy")
and you produce fun(x,y). Is there a way that lambdify can produce fun(z) with x,y=z, i.e produce the following function:
def fun(z):
x,y=z
return np.cos(x)+np.sin(y)+1
According to the documentation of lambdify you can nest the symbols in the first argument to denote unpacking in the signature:
import sympy as sym
x,y = sym.symbols('x y')
fu = sym.cos(x) + sym.sin(y) + 1
# original: signature f1(x, y)
f1 = sym.lambdify((x,y), fu)
f1(1, 2) # returns 2.4495997326938213
# nested: signature f2(z) where x,y = z
f2 = sym.lambdify([(x,y)], fu)
f2((1, 2)) # returns 2.4495997326938213
Even if this weren't possible to do within lambdify, we could define a thin wrapper that unpacks the arguments to the lambdified function (although this would be one function call slower on each call, so for fast functions that get called a lot of times this might lead to measurable impact on the runtime):
f = sym.lambdify((x,y), fu) # signature f(x,y)
def unpacking_f(z): # signature f(z) where x,y = z
return f(*z)
Of course if the function is not for a single, throw-away use in a numerical solver (such as curve fitting or minimization), it's good practice to use functools.wraps for wrappers. This would preserve the docstring automatically generated by lambdify.
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())
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. :)