英文:
Sympy system of differential equations
问题
问题
我正在为机械链接制作一个符号求解器详细信息请参考先前的问题
现在我可以使用 sympy.solve 解决大型线性方程组,但我在让求解器解决偏微分方程时遇到了困难。求解器可以解决它们,但在何时以及应该解决什么时,它会感到困惑,无法输出有用的内容。
最小代码示例:
# 尝试解决 Y=Z X=dY(Z)^3/dZ
import sympy as lib_sympy
def bad_derivative_wrong( in_x : lib_sympy.Symbol, in_y : lib_sympy.Symbol, in_z : lib_sympy.Symbol ):
l_equation = []
l_equation.append( lib_sympy.Eq( in_y, in_z ) )
l_equation.append( lib_sympy.Eq( in_x, lib_sympy.Derivative(in_y*in_y*in_y, in_z, evaluate = True) ) )
solution = lib_sympy.solve( l_equation, (in_x,in_y,), exclude = () )
return solution
def bad_derivative_unhelpful( in_x : lib_sympy.Symbol, in_y : lib_sympy.Symbol, in_z : lib_sympy.Symbol ):
l_equation = []
l_equation.append( lib_sympy.Eq( in_y, in_z ) )
l_equation.append( lib_sympy.Eq( in_x, lib_sympy.Derivative(in_y*in_y*in_y, in_z, evaluate = False) ) )
solution = lib_sympy.solve( l_equation, (in_x,in_y,), exclude = () )
return solution
def good_derivative( in_x : lib_sympy.Symbol, in_y : lib_sympy.Symbol, in_z : lib_sympy.Symbol ):
l_equation = []
l_equation.append( lib_sympy.Eq( in_y, in_z ) )
l_equation.append( lib_sympy.Eq( in_x, lib_sympy.Derivative(in_z*in_z*in_z, in_z, evaluate = True) ) )
# 这里发生的是Derivative已经解决了导数,它不是一个符号
solution = lib_sympy.solve( l_equation, (in_x,in_y,), exclude = () )
# lib_sympy.dsolve
return solution
if __name__ == '__main__':
# n_x = lib_sympy.symbols('X', cls=lib_sympy.Function)
n_x = lib_sympy.symbols('X')
n_y = lib_sympy.Symbol('Y')
n_z = lib_sympy.Symbol('Z')
print("Wrong Derivative: ", bad_derivative_wrong( n_x, n_y, n_z ) )
print("Unhelpful Derivative: ", bad_derivative_unhelpful( n_x, n_y, n_z ) )
print("Good Derivative: ", good_derivative( n_x, n_y, n_z ) )
输出:
Wrong Derivative: {Y: Z, X: 0}
Unhelpful Derivative: {Y: Z, X: Derivative(Y**3, Z)}
Good Derivative: {Y: Z, X: 3*Z**2}
问题:
我需要一种方法,可以以求解器满意的方式将偏导数符号添加到我的方程中。
例如,速度是位置随时间的导数。
例如,位置对于角度的敏感度与精度和力量有关。
英文:
Problem
I'm making a symbolic solver for mechanical links previous question for details
Right now I can get sympy.solve to solve big systems of linear equations, but I'm having an hard time making the solver resolve the partial differential equations. The solver can solve them, but it gets confused in when and what it should solve and doesn't output something useful.
Minimal Code:
#Try to solve Y=Z X=dY(Z)^3/dZ
import sympy as lib_sympy
def bad_derivative_wrong( in_x : lib_sympy.Symbol, in_y : lib_sympy.Symbol, in_z : lib_sympy.Symbol ):
l_equation = []
l_equation.append( lib_sympy.Eq( in_y, in_z ) )
l_equation.append( lib_sympy.Eq( in_x, lib_sympy.Derivative(in_y*in_y*in_y, in_z, evaluate = True) ) )
solution = lib_sympy.solve( l_equation, (in_x,in_y,), exclude = () )
return solution
def bad_derivative_unhelpful( in_x : lib_sympy.Symbol, in_y : lib_sympy.Symbol, in_z : lib_sympy.Symbol ):
l_equation = []
l_equation.append( lib_sympy.Eq( in_y, in_z ) )
l_equation.append( lib_sympy.Eq( in_x, lib_sympy.Derivative(in_y*in_y*in_y, in_z, evaluate = False) ) )
solution = lib_sympy.solve( l_equation, (in_x,in_y,), exclude = () )
return solution
def good_derivative( in_x : lib_sympy.Symbol, in_y : lib_sympy.Symbol, in_z : lib_sympy.Symbol ):
l_equation = []
l_equation.append( lib_sympy.Eq( in_y, in_z ) )
l_equation.append( lib_sympy.Eq( in_x, lib_sympy.Derivative(in_z*in_z*in_z, in_z, evaluate = True) ) )
#what happens here is that Derivative has already solved the derivative, it's not a symbol
solution = lib_sympy.solve( l_equation, (in_x,in_y,), exclude = () )
#lib_sympy.dsolve
return solution
if __name__ == '__main__':
#n_x = lib_sympy.symbols('X', cls=lib_sympy.Function)
n_x = lib_sympy.symbols('X')
n_y = lib_sympy.Symbol('Y')
n_z = lib_sympy.Symbol('Z')
print("Wrong Derivative: ", bad_derivative_wrong( n_x, n_y, n_z ) )
print("Unhelpful Derivative: ", bad_derivative_unhelpful( n_x, n_y, n_z ) )
print("Good Derivative: ", good_derivative( n_x, n_y, n_z ) )
Output:
Wrong Derivative: {Y: Z, X: 0}
Unhelpful Derivative: {Y: Z, X: Derivative(Y**3, Z)}
Good Derivative: {Y: Z, X: 3*Z**2}
Question:
I need a way to add partial derivative symbols to my equations in a way that the solver is happy to solve.
E.g. the speed is the derivative of the position over time.
E.g. the sensitivity of the position in respect to the angle is related to precision and force.
答案1
得分: 1
我明白了,我会翻译代码部分。
seek函数:
def seek(eqs, do, sol=[], strict=True):
from sympy.solvers.solvers import _invert as f
from sympy.core.compatibility import ordered
from sympy import Eq
while do and eqs:
for x in do:
for e in eqs:
if not isinstance(e, Eq):
continue
i, d = f(e.lhs - e.rhs, x)
if d != x:
continue
break
else:
if strict:
assert None # no eq could be solved for x
continue
sol.append((d, i))
eqs.remove(e)
break
do.remove(x)
if not strict:
do.extend(i.free_symbols)
do = list(ordered(do))
for _ in range(len(eqs)):
if not isinstance(eqs[_], Eq):
continue
# 避免被零除
ln, ld = eqs[_].lhs.as_numer_denom()
rn, rd = eqs[_].rhs.as_numer_denom()
eqs[_] = Eq(ln*rd, rn*ld).xreplace({x: i})
if eqs[_] == False:
raise ValueError('检测到不一致')
return sol
focus函数:
def focus(eqs, *syms, **kwargs):
"""给定“eqs”中的等式实例,解决“syms”中的符号,并尽可能地解决解中的自由符号。
当“evaluate=True”时,返回一个以“syms”为键的字典,否则返回一个包含所识别符号的列表,导致期望的符号。
示例
========
>>> focus((Eq(a, b), Eq(b + 2, c)), a)
{a: c - 2}
>>> focus((Eq(a, b), Eq(b + 2, c)), a, evaluate=False)
[(b, c - 2), (a, b)]
"""
from sympy.solvers.solvers import _invert as f
from sympy.core.compatibility import ordered
from sympy import Eq, Tuple
evaluate = kwargs.get('evaluate', True)
assert all(isinstance(i, Eq) for i in eqs)
sol = []
free = Tuple(*eqs).free_symbols
do = set(syms) & free
if not do:
return sol
eqs = list(eqs)
seek(eqs, do, sol)
assert not do
for x, i in sol:
do |= i.free_symbols
do = list(ordered(do)) # 使其规范
seek(eqs, do, sol, strict=False)
if evaluate:
while len(sol) > len(syms):
x, s = sol.pop()
for i in range(len(sol)):
sol[i] = (sol[i][0], sol[i][1].xreplace({x: s}))
for i in reversed(range(1, len(syms))):
x, s = sol[i]
for j in range(i):
y, t = sol[j]
sol[j] = y, f(y - t.xreplace({x: s}), y)[0]
simplify = kwargs.get("simplify", False)
if simplify:
for i in range(len(sol)):
sol[i] = (sol[i][0], sol[i][1].simplify())
if evaluate:
sol = dict(sol)
else:
sol = list(reversed(sol))
return sol
你的示例:
import sympy as sp
x, y, z = sp.symbols("x y z")
l_equation = []
l_equation.append(sp.Eq(y, z))
l_equation.append(sp.Eq(x, sp.Derivative(y**3, z)))
solution = focus(l_equation, x, y, simplify=True)
print(solution) # {y: z, x: 3*z**2}
如果你还需要其他帮助,请告诉我。
英文:
It took a while to find the answer, but I found this solution that helped out. Essentially, there is a (currently) open git issue that shares seek
and focus
functions. Solving your equations using the focus
function will give you the x
and y
solutions as functions of z
only. Since you aren't importing the entire sympy library (i.e. you aren't doing from sympy import *
), which is how I also run sympy, we need to add import lines for Eq
and Tuple
to the functions, which I did below.
I also made a change that allows you to pass optionally pass simplify
to focus
. If you set simplify=True
, it will simplify the results. This is what you want, otherwise, it will return {y: z, x: Derivative(z**3, z)}
.
seek:
def seek(eqs, do, sol=[], strict=True):
from sympy.solvers.solvers import _invert as f
from sympy.core.compatibility import ordered
from sympy import Eq
while do and eqs:
for x in do:
for e in eqs:
if not isinstance(e, Eq):
continue
i, d = f(e.lhs - e.rhs, x)
if d != x:
continue
break
else:
if strict:
assert None # no eq could be solved for x
continue
sol.append((d, i))
eqs.remove(e)
break
do.remove(x)
if not strict:
do.extend(i.free_symbols)
do = list(ordered(do))
for _ in range(len(eqs)):
if not isinstance(eqs[_], Eq):
continue
# avoid dividing by zero
ln, ld = eqs[_].lhs.as_numer_denom()
rn, rd = eqs[_].rhs.as_numer_denom()
eqs[_] = Eq(ln*rd, rn*ld).xreplace({x: i})
if eqs[_] == False:
raise ValueError('inconsistency detected')
return sol
focus:
def focus(eqs, *syms, **kwargs):
"""Given Equality instances in ``eqs``, solve for symbols in
``syms`` and resolve as many of the free symbols in the solutions
as possible. When ``evaluate=True`` a dictionary with keys being
``syms`` is returned, otherwise a list of identified symbols
leading to the desired symbols is given.
Examples
========
>>> focus((Eq(a, b), Eq(b + 2, c)), a)
{a: c - 2}
>>> focus((Eq(a, b), Eq(b + 2, c)), a, evaluate=False)
[(b, c - 2), (a, b)]
"""
from sympy.solvers.solvers import _invert as f
from sympy.core.compatibility import ordered
from sympy import Eq, Tuple
evaluate = kwargs.get('evaluate', True)
assert all(isinstance(i, Eq) for i in eqs)
sol = []
free = Tuple(*eqs).free_symbols
do = set(syms) & free
if not do:
return sol
eqs = list(eqs)
seek(eqs, do, sol)
assert not do
for x, i in sol:
do |= i.free_symbols
do = list(ordered(do)) # make it canonical
seek(eqs, do, sol, strict=False)
if evaluate:
while len(sol) > len(syms):
x, s = sol.pop()
for i in range(len(sol)):
sol[i] = (sol[i][0], sol[i][1].xreplace({x: s}))
for i in reversed(range(1, len(syms))):
x, s = sol[i]
for j in range(i):
y, t = sol[j]
sol[j] = y, f(y - t.xreplace({x: s}), y)[0]
simplify = kwargs.get("simplify", False)
if simplify:
for i in range(len(sol)):
sol[i] = (sol[i][0], sol[i][1].simplify())
if evaluate:
sol = dict(sol)
else:
sol = list(reversed(sol))
return sol
Your example:
import sympy as sp
x, y, z = sp.symbols("x y z")
l_equation = []
l_equation.append(sp.Eq(y, z))
l_equation.append(sp.Eq(x, sp.Derivative(y**3, z)))
solution = focus(l_equation, x, y, simplify=True)
print(solution) # {y: z, x: 3*z**2}
As indicated by my explanation above, I give credit to those who originally developed this solution, I've only made some minor changes to suit the needs of this problem.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论