Why does sympy.diff not differentiate sympy polynomials as expected? - python

I am trying to figure out why sympy.diff does not differentiate sympy polynomials as expected. Normally, sympy.diff works just fine if a symbolic variable is defined and the polynomial is NOT defined using sympy.Poly. However, if the function is defined using sympy.Poly, sympy.diff does not seem to actually compute the derivative. Below is a code sample that shows what I mean:
import sympy as sy
# define symbolic variables
x = sy.Symbol('x')
y = sy.Symbol('y')
# define function WITHOUT using sy.Poly
f1 = x + 1
# define function WITH using sy.Poly
f2 = sy.Poly(x + 1, x, domain='QQ')
# compute derivatives and return results
df1 = sy.diff(f1,x)
df2 = sy.diff(f2,x)
print('f1: ',f1)
print('f2: ',f2)
print('df1: ',df1)
print('df2: ',df2)
This prints the following results:
f1: x + 1
f2: Poly(x + 1, x, domain='QQ')
df1: 1
df2: Derivative(Poly(x + 1, x, domain='QQ'), x)
Why does sympy.diff not know how to differentiate the sympy.Poly version of the polynomial? Is there a way to differentiate the sympy polynomial, or a way to convert the sympy polynomial to the form that allows it to be differentiated?
Note: I tried with different domains (i.e., domain='RR' instead of domain='QQ'), and the output does not change.

This appears to be a bug. You can get around it by calling diff directly on the Poly instance. Ideally calling the function diff from the top level sympy module should yield the same result as calling the method diff.
In [1]: from sympy import *
In [2]: from sympy.abc import x
In [3]: p = Poly(x+1, x, domain='QQ')
In [4]: p.diff(x)
Out[4]: Poly(1, x, domain='QQ')
In [5]: diff(p, x)
Out[5]: Derivative(Poly(x + 1, x, domain='QQ'), x)
In [6]: diff(p, x).doit()
Out[6]: Derivative(Poly(x + 1, x, domain='ZZ'), x)

Related

Solving ill-posed non-linear equations numerically in python/SymPy

I'm trying to get a solution by running the code below.
Python just "hangs" and won't find a numeric solution. I can use an app on my phone (Desmos) to graph the functions and find a numeric solution easily, 0.024. Does python have limitations when solving for 2 decimal places?
import sympy
x = sympy.symbols('x')
e_1 = x**-0.5
e_2 = -2*sympy.log(0.0001*3.7**-1*0.05**-1+2.51*350000**-1*x**-0.5, 10)
sol = sympy.solve(e_2 - e_1, x, 0.024)
num = float(sol[0])
print(num)
Usually, nsolve is the SymPy tool used to numerically solve an equation (or a system of equations). However, I wasn't able to use: it kept raising errors. The problem is that your function is defined on a very small region, and the zero is very close to the boundary:
So, in this case we can try numerical root finding techniques. Again, some tools might fail, but after a few tries I've found that bisect works fine:
from sympy import *
from scipy.optimize import root, brentq, bisect
x = symbols('x')
# you didn't provide the diameter, so I've computed it based on your expected solution
d = 1.56843878182221
e_1 = x**-0.5
e_2 = -2 * log(0.00013 * 7-1*d-1+2.51350000**-1*x**-0.5, 10)
# convert the symbolic expression to a numerical function
ff = lambdify(x, e_1 - e_2)
root, output = bisect(ff, 0.023, 0.025, full_output=True)
print(root)
# 0.024000000001862646
print(output)
# converged: True
# flag: 'converged'
# function_calls: 32
# iterations: 30
# root: 0.024000000001862646
The fixed point method is a great one to use for situations like this. Or at least the principles of transforming the equation into a compatible form can benefit standard solvers by providing a less ill-behaved form of the function.
You have an ill-defined equation in the form y - g(y) where y = 1/sqrt(x). So let's get the inverse of g (call it G) so we can solve G(y) - G(g(y)) = G(y) - y instead.
>>> g = e_2.subs(1/x**.5, y)
>>> d = Dummy()
>>> G = solve(g - d, y)[0].subs(d, y)
>>> nsolve(G - y, 6)
6.45497224367903
>>> solve(1/x**.5 - _, dict=True)
{x: 0.024}
The process of rearranging an equation f(x) into form x - g(x) could probably use a built-in method in SymPy, but it's not too hard to do this by replacing each x with a dummy variable, solving for it, and then replacing the dummy symbols with x again. Different g will be more favorable for finding different roots as can be seen in the example below where the purple dashed line is good for finding the root near 1 while the solid blue is better near the smaller root.
Here is a possibility for a "fixed point form" function:
def fixedpoint_Eqs(eq, x=None):
"""rearrange to give eq in form x = g(x)"""
f = eq.free_symbols
fp = []
if x is None:
assert len(f) == 1, 'must specify x in this case'
x = list(f)[0]
Xeq = eq.replace(lambda _:_ == x, lambda _:Dummy())
X = Xeq.free_symbols - f
reps = {xi: x for xi in X}
for xi in X:
try:
g = solve(Xeq, xi, dict=True)
if len(g) != 1:
raise NotImplementedError
fp.append(Eq(x, g[0][xi].xreplace(reps)))
except NotImplementedError:
pass
return fp
>>> fixedpoint_Eqs(x+exp(x)+1/x-5)
Eq(x, -1/(x + exp(x) - 5))
Eq(x, -exp(x) + 5 - 1/x)
Eq(x, log(-x + 5 - 1/x))

How to resolve integration function not integrating correctly?

I am trying to build a few simple operations such as a derivative and integral function to operate on lambda functions because sympy and scipy were struggling to integrate some things that I was passing to them.
The derivative function does not give me any issues and looks to return the derivative of the input function when plotted, but the integral function does not return the same, and does not plot the correct integral of the input.
import matplotlib.pyplot as plt
import numpy as np
from phys_func import func
sr = [-10,10]
x = np.linspace(sr[0],sr[1], 100)
F = lambda x: x**2
f = func.I(F,x)
plt.plot(x,F(x), label = 'F(x)')
plt.plot(x,f(x), label = 'f(x)')
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.show()
The integration function that does not work:
def I(F,x):
dx = (x[len(x)-1] - x[0])/len(x)
return lambda x : 0.5*( F(x+dx) + F(x) )*dx
The derivative function that works:
def d(f,x):
dx = (x[len(x)-1]-x[0])/len(x)
return lambda x: (f(x+dx)-f(x))/dx
Can anyone lend me a hand please?
You cannot find the anti derivative of a function numerically with out knowing the value of the anti derivative at a single point. Suppose if you fix the value of the antiderivative function value at x =a to be 0 (and given function is continuous from [a,x]) , then we can use definite integrals. For this function, let us take a=0 (i.e 0 is a root of anti derivative function), so you can do a definite integral from [0,x]. Also, your integration function is wrong. You need to sum all the 0.5*( F(x+dx) + F(x) )*dx elements from 0 to x to get the definite integral.
You can modify I(f,x) as follows
def I(F1): # N is number of intervals
return lambda x, N: np.sum( 0.5*( F1(np.linspace(0,x,num=N)+ (x)/N ) + F1(np.linspace(0,x,num=N)))*(x)/N)
In [1]: import numpy as np
In [2]: def I(F1): # N is number of intervals
...: return lambda x, N: np.sum( 0.5*( F1(np.linspace(0,x,num=N)+ (x)/N
...: ) + F1(np.linspace(0,x,num=N)))*(x)/N)
...:
In [3]: F = lambda x: x**2
In [4]: x_ran = np.linspace(-10,10, 100)
In [5]: y = I(F)
In [6]: y_ran = []
In [7]: for i in x_ran:
...: y_ran.append(y(i,100))
In [8]: plt.plot(x_ran,y_ran)
In [9]: plt.show()

Getting the derivative of a function as a function with sympy (for later evaluation and substitution)

I want to work with generic functions as long as possible, and only substitute functions at the end.
I'd like to define a function as the derivative of another one, define a generic expression with the function and its derivative, and substitute the function at the end.
Right now my attempts is as follows, but I get the error 'Derivative' object is not callable:
from sympy import Function
x, y, z = symbols('x y z')
f = Function('f')
df = f(x).diff(x) # <<< I'd like this to be a function of dummy variable x
expr = f(x) * df(z) + df(y) + df(0) # df is unfortunately not callable
# At the end, substitute with example function
expr.replace(f, Lambda(X, cos(X))) # should return: -cos(x)*sin(z) - sin(y) - sin(0)
I think I got it to work with integrals as follows:
I= Lambda( x, integrate( f(y), (y, 0, x))) but that won't work for derivatives.
If that helps, I'm fine restricting myself to functions of a single variable for now.
As a bonus, I'd like to get this to work with any combination (products, derivatives, integrals) of the original function.
It's pretty disappointing that f.diff(x) doesn't work, as you say. Maybe someone will create support it sometime in the future. In the mean time, there are 2 ways to go about it: either substitute x for your y, z, ... OR lambdify df.
I think the first option will work more consistently in the long run (for example, if you decide to extend to multivariate calculus). But the expr in second option is far more natural.
Using substitution:
from sympy import *
x, y, z = symbols('x y z')
X = Symbol('X')
f = Function('f')
df = f(x).diff(x)
expr = f(x) * df.subs(x, z) + df.subs(x, y) + df.subs(x, 0)
print(expr.replace(f, Lambda(X, cos(X))).doit())
Lambdifying df:
from sympy import *
x, y, z = symbols('x y z')
X = Symbol('X')
f = Function('f')
df = lambda t: f(t).diff(t) if isinstance(t, Symbol) else f(X).diff(X).subs(X, t)
expr = f(x) * df(z) + df(y) + df(0)
print(expr.replace(f, Lambda(X, cos(X))).doit())
Both give the desired output.

Having trouble with using sympy subs command when trying to solve a function at an x value of 0

I have a function [ -4*x/sqrt(1 - (1 - 2*x^2)^2) + 2/sqrt(1 - x^2) ] that I need to evaluate at x=0. However, whenever you graph this function, for some interval of y there are many y-values at x=0. This leads me to think that the (subs) command can only return one y-value. Any help or elaboration on this? Thank you!
Here's my code if it might help:
x = symbols('x')
f = 2*asin(x) # f(x) function
g = acos(1-2*x**2) # g(x) function
eq = diff(f-g) # evaluating the derivative of f(x) - g(x)
eq.subs(x, 0) # substituting 0 for x in the derivative of f(x) - g(x)
After I run the code, it returns NaN, which I assume is because substituting in 0 for x returns not a single number, but a range of numbers.
Here is the graph of the function to be evaluated at x=0:
You should always give SymPy as many assumptions as possible. For example, it can't pull an x**2 out of a sqrt because it thinks x is complex.
A factorization an then a simplification solves the problem. SymPy can't do L'Hopital on eq = A + B since it does not know that both A and B converge. So you have to guide it a little by bringing the fractions together and then simplifying:
from sympy import *
x = symbols('x', real=True)
f = 2*asin(x) # f(x) function
g = acos(1-2*x**2) # g(x) function
eq = diff(f-g) # evaluating the derivative of f(x) - g(x)
eq = simplify(factor(eq))
print(eq)
print(limit(eq, x, 0, "+"))
print(limit(eq, x, 0, "-"))
Outputs:
(-2*x + 2*Abs(x))/(sqrt(1 - x**2)*Abs(x))
0
4
simplify, factor and expand do wonders.

How to solve "Absolute values cannot be inverted in the complex domain" error in sympy

I have the following equation:
Eq(5*Abs(4*x+2)+6,56).
What I am trying to do is solve for x = -3 for the math question 5 |4x+2|+6=56, but I keep getting the
"Absolute values cannot be inverted in the complex domain"
error in sympy.
Is there a way around this?
You must specify that x is a real-valued variable. You can do that when you define the variable as follows.
import sympy as sp
x = sp.symbols('x', real = True)
eq = sp.Eq(5*sp.Abs(4*x+2)+6,56)
sol = sp.solve(eq, x)
print(sol)
[-3, 2]
EDIT: The sympy.solveset function can be used instead of sympy.solve. In that case, you need to explicitly state that you are solving over the domain of reals. By doing so, you do not have to define your variable as real.
import sympy as sp
x = sp.symbols('x') # implies that x is complex
eq = sp.Eq(5*sp.Abs(4*x+2)+6,56)
sol = sp.solveset(eq, x, domain=sp.S.Reals)
print(sol)
{-3, 2}

Categories