SymPy 微分方程系统

huangapple go评论74阅读模式
英文:

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.

huangapple
  • 本文由 发表于 2023年6月29日 14:02:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/76578403.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定