Sympy trouble with simplifying partial derivatives - python

I do the following to generate an expression in Sympy:
Create some matrix Q_{ij} which holds some functions \eta, \mu, \nu, of x and y.
Sum over indices and take some partial derivatives.
Substitute some simple expressions for \eta, \mu, and \nu (say \sin(x)*\cos(y)).
Try to simplify the expression so that it explicitly calculates the partial derivatives of the simple expressions.
When I do this, it gives me the following error:
NotImplementedError: Improve MV Derivative support in collect
The specific code that I used is:
from sympy import *
# Set up system and generate functions
x, y, z = symbols('x y z')
i, j, k, m, p = symbols('i j k m p')
xi = Matrix([x, y, z])
lims = range(0, 3)
eta = Function('eta', real=True)(x, y)
mu = Function('mu', real=True)(x, y)
nu = Function('nu', real=True)(x, y)
Q = Matrix([[2/sqrt(3)*eta, nu, 0],
[nu, -1/sqrt(3)*eta + mu, 0],
[0, 0, -1/sqrt(3)*eta - mu]])
# Create complicated expression of partial derivatives
Phi_L1 = -sum(Eijk(3, p + 1, i + 1)
*diff(diff(diff(Q[k, l], xi[j]), xi[j]), xi[p])
*diff(Q[k, l], xi[i])
for i in lims
for j in lims
for k in lims
for l in lims
for p in lims
)
Phi_L1 = simplify(Phi_L1)
# Choose example functions and try to evaluate expression explicitly
eta1 = sin(x)*cos(y)
mu1 = sinh(x)*cosh(y)
nu1 = x**2*y**2
expr1 = Phi_L1.subs(eta, eta1).subs(mu, mu1).subs(nu, nu1)
simplify(expr1)
I couldn't find a simpler example that gives the same error. For example, the following works as intended:
f = Function('f', real=True)(x, y)
expr = diff(diff(f, x), y)
simplify(expr.subs(f, sinh(x)*cosh(y)))

'collect' and 'simplify' have known problems with higher order partial derivatives https://github.com/sympy/sympy/issues/9068 outlines the issue
The example shown is
import sympy as sp
x, y = sp.symbols("x y")
f, g = sp.symbols("f g", cls=sp.Function, args=(x,y))
f, g = f(), g()
expr1 = g.diff(x, 2) + f.diff(x, 2) + 5*f.diff(x, 2) + 2*f.diff(x) + 9*f.diff(x)
expr2 = g.diff(x, 2) + f.diff(x, 2) + 5*f.diff(x, 2) + 2*f.diff(x) + 9*f.diff(x) + g.diff(x).diff(y) + f.diff(x).diff(y)
which works correctly and show the expected output for both expr1 and expo2
sp.collect(expr1, f) works wonderfully but sp.collect(expr2, f) fails with the known error as the implementation is not finished...

This does not address the error (which is apparently a bug), but one work around for the case of explicitly taking derivatives of simple functions is to just use the doit() function. In this case, that would look like (following the code posted in the question):
simplify(expr1.doit())

Related

Use `sympy.Order` with functions, instead of symbols

I have a problem like this f(u(x,y), v(x,y)) = 0. For a simple example we could choose f=u^2*v. I want to perturb the state with u=u_0+du,v=v_0+dv.
Just doing it in sympy like:
import sympy as sp
x, y = sp.symbols("x, y")
u0, v0 = sp.symbols("u_0, v_0")
du = sp.Function("\\delta u")(x, y)
dv = sp.Function("\\delta v")(x, y)
u = u0 + du
v = v0 + dv
f = u**2 * v
print(sp.expand(u**2 * v))
I get u_0**2*v_0 + u_0**2*\delta v(x, y) + 2*u_0*v_0*\delta u(x, y) + 2*u_0*\delta u(x, y)*\delta v(x, y) + v_0*\delta u(x, y)**2 + \delta u(x, y)**2*\delta v(x, y)
Is there a way to tell sympy that any product of deltas can be zero?
I tried using sp.Order but it doesn't work where the series is a power of some function
Thanks
I usually approach these tasks from an expression manipulation point of view: knowing what I am looking for I seek to make that change (as opposed to letting something like series do what it should do). In this case you are looking for terms that have a product of functions. Such terms could be a Power (like f**2) or a Mul (like x*f1*f2). So something like this could be done:
def funcpow(x):
rv = 0
if x.is_Mul or x.is_Pow:
for i in Mul.make_args(x):
b,e=i.as_base_exp()
if b.is_Function:
rv += e
return rv
>>> eq.replace(lambda x: funcpow(x)>1, lambda x: 0)
u_0**2*v_0 + u_0**2*\\delta v(x, y) + 2*u_0*v_0*\\delta u(x, y)
Often, the conditions are placed in the call to replace but since there are several I made a helper function instead.

Curve translation in Python does not reach expected value

Suppose I have two curves, f(x) and g(x), and I want to evaluate if g(x) is a translation of f(x).
I used Sympy Curve to do the job with the function translate. However, I need help to reach the correct result. Consider the two functions:
f(x) = -x^2 and g(x) = -(x+5)^2 + 8
Notice that g is vertically translated by 8 and horizontally translated by 5. Why at is not equal to b in the following Python code?
from sympy import expand, Symbol, Curve, oo
x = Symbol('x')
f = -x**2
g = -(x+5)**2+8
a = Curve((x, f), (x, -oo, oo))
at = a.translate(5,8)
b = Curve((x, g), (x, -oo, oo))
a, at, b, at == b
>>> (Curve((x, -x**2), (x, -10, 10)),
Curve((x + 5, 8 - x**2), (x, -10, 10)),
Curve((x, 8 - (x + 5)**2), (x, -10, 10)),
False)
How could I make this analysis work using this or any other method?
Curve is probably not the right way to do this. If you have univariate functions for which you want to know a x and y translation will make them equal, you could use something like the following:
>>> dx,dy = Symbols('dx dy')
>>> eq = Eq(f.subs(x,x-dx)+dy)
>>> solve_undetermined_coeffs(eq,g),(dx,dy),x)
[(-5, 8)]
If there are no values of dx and dy that will solve the equality, then an empty list will be returned.
Thanks to #smichr for the reference about solve_undetermined_coeffs. Here you can find a full answer to my initial problem in Python 3.8.10 and Sympy 1.11.1.
from sympy import symbols, Eq, solve_undetermined_coeffs
x, dx, dy = symbols('x dx dy')
f = -x**2
g = -(x+5)**2+8
eq = Eq(f.subs(x,x-dx)+dy,g)
solve_undetermined_coeffs(eq,[dx,dy],x)

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.

Python; Solving equation equal to zero

How I can equate an equation to zero then solve it (the purpose is to eliminate the denominator).
y=(x**2-2)/3*x
In Matlab this works:
solution= solve(y==0,x)
but not in python.
from sympy import *
x, y = symbols('x y')
y=(x**2-2)/3*x
# set the expression, y, equal to 0 and solve
result = solve(Eq(y, 0))
print(result)
Another solution:
from sympy import *
x, y = symbols('x y')
equation = Eq(y, (x**2-2)/3*x)
# Use sympy.subs() method
result = solve(equation.subs(y, 0))
print(result)
Edit (even simpler):
from sympy import *
x, y = symbols('x y')
y=(x**2-2)/3*x
# solve the expression y (by default set equal to 0)
result = solve(y)
print(result)
If you want only to eliminate the denominator, then you can split it into numerator and denominator. If the equation is already appear as a fraction and you want the numerator then
>>> y=(x**2-2)/(3*x); y # note parentheses around denom, is that what you meant?
(x**2 - 2)/(3*x)
>>> numer(_)
x**2 - 2
But if the equation appears as a sum then you can put it over a denominator and perhaps factor to identify numerator factors that must be zero in order to solve the equation:
>>> y + x/(x**2+2)
x/(x**2 + 2) + (x**2 - 2)/(3*x)
>>> n, d = _.as_numer_denom(); (n, d)
(3*x**2 + (x**2 - 2)*(x**2 + 2), 3*x*(x**2 + 2))
>>> factor(n)
(x - 1)*(x + 1)*(x**2 + 4)
>>> solve(_)
[-1, 1, -2*I, 2*I]
You don't have to factor the numerator before attempting to solve, however. But I sometimes find it useful when working with a specific equation.
If you have an example of an equation that is solved quickly elsewhere but not in SymPy, please post it.

Numerical and analytical equation solving in python or matlab

Let's suppose that I have two transcendental functions f(x, y) = 0 and g(a, b) = 0.
a and b depend on y, so if I could solve the first equation analytically for y, y = f(x), I could have the second function depending only on x and thus solving it numerically.
I prefer to use python, but if matlab is able to handle this is ok for me.
Is there a way to solve analytically trascendent functions for a variable with python/matlab? Taylor is fine too, as long as I can choose the order of approximation.
I tried running this through Sympy like so:
import sympy
j, k, m, x, y = sympy.symbols("j k m x y")
eq = sympy.Eq(k * sympy.tan(y) + j * sympy.tan(sympy.asin(sympy.sin(y) / x)), m)
eq.simplify()
which turned your equation into
Eq(m, j*sin(y)/(x*sqrt(1 - sin(y)**2/x**2)) + k*tan(y))
which, after a bit more poking, gives us
k * tan(y) + j * sin(y) / sqrt(x**2 - sin(y)**2) == m
We can find an expression for x(y) like
sympy.solve(eq, x)
which returns
[-sqrt(j**2*sin(y)**2/(k*tan(y) - m)**2 + sin(y)**2),
sqrt(j**2*sin(y)**2/(k*tan(y) - m)**2 + sin(y)**2)]
but an analytic solution for y(x) fails.

Categories