使用sagemath或sympy将n阶微分方程简化为一阶方程组

时间:2014-08-31 09:03:23

标签: math numerical-methods sympy ode sage

我想将n阶常微分方程简化为一阶方程组。这是为数值分析做准备。我使用Sympy和Sagemath作为计算机代数,但我没有找到任何函数来进行这种类型的缩减。我不确定是否有其他人可以指出Sympy或Sagemath中是否存在此功能。

这方面的一个例子是减少等式:

x''' - 2x'' + x' = 0 

到一阶方程组:

[0  1 0 
 0  0 1 
 0 -1 2]

4 个答案:

答案 0 :(得分:5)

据我所知,SymPy没有直接执行此功能的功能,但手动操作非常简单。

我假设您总是希望您的两个附加公式为y = x'z = y'

首先,让我们创建ODE(在SymPy中,表达式自动假设为零,所以为简化起见,让我们不要理会= 0部分)。我假设您的自变量为t

In [4]: t = symbols('t')

In [7]: x, y, z = symbols('x y z', cls=Function)

In [6]: ode = x(t).diff(t, t) - 2*x(t).diff(t) + x(t)

In [13]: ode = x(t).diff(t, 3) - 2*x(t).diff(t, t) + x(t).diff(t)

In [14]: ode
Out[14]:
               2           3
d             d           d
──(x(t)) - 2⋅───(x(t)) + ───(x(t))
dt             2           3
             dt          dt

现在,如果我们将x'替换为y

In [15]: ode.subs(x(t).diff(t), y(t))
Out[15]:
                      2
         d           d
y(t) - 2⋅──(y(t)) + ───(y(t))
         dt           2
                    dt

我们发现它还会将x''替换为y'。因此,让y'代替z

In [16]: ode = ode.subs(x(t).diff(t), y(t)).subs(y(t).diff(t), z(t))

In [17]: ode
Out[17]:
                d
y(t) - 2⋅z(t) + ──(z(t))
                dt

现在我们的系统[x' y' z']

In [20]: Matrix([y(t), z(t), solve(ode, z(t).diff(t))[0]])
Out[20]:
⎡     y(t)     ⎤
⎢              ⎥
⎢     z(t)     ⎥
⎢              ⎥
⎣-y(t) + 2⋅z(t)⎦

请注意,我们已经知道x' = yy' = z,因此我们只是直接使用这些内容,但我们使用solve()来代替z'获取替换的ODE。

如果你想要系数,一个简单的技巧是采用雅可比行列式:

In [23]: M = Matrix([y(t), z(t), solve(ode, z(t).diff(t))[0]]).jacobian([x(t), y(t), z(t)])

In [24]: M
Out[24]:
⎡0  1   0⎤
⎢        ⎥
⎢0  0   1⎥
⎢        ⎥
⎣0  -1  2⎦

我将把它包装成单个函数ode_to_system(ode, [x(t), y(t), z(t)])的任务作为练习给读者。

答案 1 :(得分:3)

问题是,如何将微分方程编码为字符串。因为编码已经比写下一阶系统更复杂了。

一般手动程序是设置x1 = x,x2 = x',x3 = x''然后注意到

x1'=x'=x2
x2'=x''=x3
x3'=x'''= 2*x'' - x' = 2*x3 - x2

然后将生成的系统转换为矩阵形式。

另请参见多项式的伴随矩阵,这是(最多换位)也是您为高阶线性微分方程的系统矩阵获得的一般形式。

答案 2 :(得分:3)

我写了一个实验库来处理常微分方程组:

https://github.com/bjodah/symodesys

它基于同情,不幸的是我没有写太多文档,但我提供了很多例子。我会按如下方式处理你的等式:

from sympy import *
from symodesys.odesys import AnyOrderODESystem

t = Symbol('t')
x = Function('x')(t)

D1x = x.diff(t)
D2x = x.diff(t, 2)
D3x = x.diff(t, 3)

expr = Eq(D3x, 2*D2x - D1x)

odesys = AnyOrderODESystem.from_list_of_eqs([expr])
print(odesys.all_depv)
redsys = odesys.reduce_to_sys_of_first_order()
print(redsys.all_depv)
print(redsys.f)

哪个输出:

[x(t)]
[x(t), x_h1(t), x_h2(t)]
OrderedDict([(x(t), x_h1(t)), (x_h1(t), x_h2(t)), (x_h2(t), -x_h1(t) + 2*x_h2(t))])

添加一些额外的行为您提供了一个尝试初始值问题的gui (参见溶液曲线作为初始值的函数)

from symodesys.gui import get_chaco_viewer
from collections import defaultdict
y0 = defaultdict(int)
y0[redsys['x']] = 3.14
params = {}
t0, tend, N = 0, 10, 100
viewer = get_chaco_viewer(redsys, y0, params, t0, tend, N)
viewer.configure_traits()
viewer.clean_up()

给你:

symodesys gui

安装一些依赖项有点棘手,如果需要帮助则添加注释!

答案 3 :(得分:1)

我不使用Sympy或Sagemath。但是在他们的API文档中寻找拉普拉斯或Z变换。

  • 如果找到,那么你的工作就会减少
  • 如果没有,你必须自己找lib或编码

使用拉普拉斯解决差分系统

  • 我没有使用它很长一段时间所以用一些数学书检查所有这些!!!
  • 无论如何,如果我没记错的话
  • 拉普拉斯变换将积分函数转换为线性函数(时域到s域)
  • 你的差异函数必须有连续的推导/积分才能使这项工作
  • 解决您的问题:

    1. 通过整合整个事物将所有差异转换为积分
    2. 应用拉普拉斯变换
      • 这会将差分系统转换为多项式系统
      • wiki example
    3. 求解方程的多项式系统
    4. 应用逆拉普拉斯变换
      • 这会将部分结果转换为您的解决方案的结果
    5. 通过probem定义的边缘情况解决集成常量
  • 还可以查看here了解其他示例

  • 关于这个话题有很多东西只是google

通过Z

解决差分系统
  • 从未这样做,但如果与拉普拉斯变换的解决方案不同,则应该相似