我正在使用SymPy版本0.7.3,并在使用dsolve函数时遇到一些问题。当输入方程有太多参数时,dsolve似乎有困难。
我尝试解决以下等式:
from sympy import *
p = Function('p')
t, u1, u2, u3, u4, u5 = symbols('t u1 u2 u3 u4 u5')
eq = Eq(Derivative(p(t),t), -(u3 + u4)*p(t) + exp(-t*(u1 + u2)))
eq
Out: Derivative(p(t), t) == (-u3 - u4)*p(t) + exp(-t*(u1 + u2))
%time dsolve(eq)
得到了:
CPU times: user 213.11 s, sys: 0.00 s, total: 213.11 s
Wall time: 213.12 s
p(t) == (C1 + Piecewise((t*u1/(u1 + u2) + t*u2/(u1 + u2), u3 == u1 + u2 - u4), (-exp(t*u3)*exp(t*u4)/(u1*exp(t*u1)*exp(t*u2) + u2*exp(t*u1)*exp(t*u2) - u3*exp(t*u1)*exp(t*u2) - u4*exp(t*u1)*ex
p(t*u2)), True)))*exp(-t*(u3 + u4))
(花了213.12秒!)
然后我用u5替换了u1 + u2:
eq = Eq(Derivative(p(t),t), -(u3 + u4)*p(t) + exp(-t*(u1 + u2))).subs(u1+u2, u5)
eq
Out:Derivative(p(t), t) == (-u3 - u4)*p(t) + exp(-t*u5)
%time dsolve(eq)
得到了:
CPU times: user 1.62 s, sys: 0.00 s, total: 1.62 s
Wall time: 1.62 s
p(t) == (C1 + Piecewise((t, u3 == -u4 + u5), (exp(t*u3)*exp(t*u4)/(u3*exp(t*u5) + u4*exp(t*u5) - u5*exp(t*u5)), True)))*exp(-t*(u3 + u4))
(仅1.62秒!)
我尝试过使用不同的提示,但没有帮助......
我也注意到在更复杂的函数中,dsolve会崩溃,但是当替换一些常量参数时,它会很快运行。
你知道这种现象的原因是什么吗? 有没有办法自动解决这个问题?
答案 0 :(得分:2)
我注意到Mathematica存在同样的问题,限制表达式中的符号数是非常有利的。我认为其原因在于大多数符号计算工具首先通过机械方式应用一般配方来解决问题,然后尽可能简化结果。
在一般解决方案中,简化器可能很难恢复某些符号仅在给定组合中出现,因此可以被视为单个符号。因此,简化器不必要地处理更大的搜索空间。此外,它必须确保它正确处理所有边界情况(u1
可能< 0,> 0,= 0,复杂,......?)
我发现在将问题提供给符号解算器之前,尽可能多地进行手工简化是非常有利的。手动将变量分组(如在您的示例中)是一种有用的技术。另一种技术是计算规范化,或将一个参数设置为1.例如,在处理多项式a x^2 + b x + c
时,问题x^2 + B x + C
大部分时间与我们相同。 (因为我们确实知道a != 0
,但是忘了告诉求解器,对吗?...)但是对于求解器,如果符号变量的数量减少了1,它会产生很大的不同。
在某些时候,这些符号解算器肯定会变得足够聪明,可以在尝试解决问题之前将变量组合在一起。但是,目前看来仍然需要人为干预。
另一方面,很难想象符号解算器会自动识别简化问题的更复杂的变换,例如从笛卡尔坐标转换到极坐标,或者更改变量,例如l=x+iy
, r=x-iy
,这些都不是很明显,但已知对某些问题有利。
更新
我们可以为此问题做的最好的事情是设置a = u3 + u4
和a + b = u1 + u2
。除了将参数数量从4减少到2之外,特殊情况现在出现在b == 0
时,因此我们可以轻松地指示求解器忽略它:
from sympy import *
p = Function('p')
t, a = symbols('t a')
b = symbols('b', nonzero=True)
eq = Eq(Derivative(p(t),t), -a*p(t) + exp(-(a + b)*t))
dsolve(eq)
# -> p(t) == (C1 - exp(-b*t)/b)*exp(-a*t)
# (0.75 s)
因此,通过避免特殊情况帮助解算器将解决方案时间再缩短一半(我的系统上的时序与您的相似)。如果特殊情况b == 0
实际上是相关的,那么可以很容易地从exp(-b*t) ~ 1 - b*t
的泰勒级数展开中恢复它。
通常,指定变量是实数,非零,严格大于零等等也非常有用,以避免在特殊情况下使解算器挂起。有时,为x < 0
,x > 0
和x == 0
单独解决问题实际上更好(避免臭名昭着的sqrt(x^2)
表达式,解算器无法在不知道符号的情况下进一步简化x
)。
答案 1 :(得分:2)
如果您使用"1st_linear_Integral"
提示,问题会更清楚一些,它会返回将作为未评估积分的内容(我使用1st_linear
,因为这是classify_ode(eq)
返回的第一个方法,意味着它是dsolve
默认使用的那个:
In [61]: dsolve(Eq(Derivative(p(t),t), -(u3 + u4)*p(t) + exp(-t*(u1 + u2))), hint='1st_linear_Integral')
Out[61]:
⎛ ⌠ ⎞
⎜ ⎮ ⌠ ⎟ ⌠
⎜ ⎮ ⎮ (u₃ + u₄) dt ⎟ -⎮ (u₃ + u₄) dt
⎜ ⎮ -t⋅u₁ -t⋅u₂ ⌡ ⎟ ⌡
p(t) = ⎜C₁ + ⎮ ℯ ⋅ℯ ⋅ℯ dt⎟⋅ℯ
⎝ ⌡ ⎠
In [62]: dsolve(Eq(Derivative(p(t),t), -(u3 + u4)*p(t) + exp(-t*(u1 + u2))).subs(u1+u2, u5), hint='1st_linear_Integral')
Out[62]:
⎛ ⌠ ⎞
⎜ ⎮ ⌠ ⎟ ⌠
⎜ ⎮ ⎮ (u₃ + u₄) dt ⎟ -⎮ (u₃ + u₄) dt
⎜ ⎮ -t⋅u₅ ⌡ ⎟ ⌡
p(t) = ⎜C₁ + ⎮ ℯ ⋅ℯ dt⎟⋅ℯ
⎝ ⌡ ⎠
集成算法与exp(a*x)*exp(b*x)
存在问题,而exp((a + b)*x)
没有问题。基本上有一个更快的算法,其范围更有限,首先被调用,然后是一个较慢的算法,如果快速的算法失败则会调用更大的范围。快速的人可以处理exp((a + b)*x)
,但目前不是exp(a*x)*exp(b*x)
。
此特定情况下的简单解决方法是使用powsimp
将指数合并在一起:
In [67]: a = dsolve(Eq(Derivative(p(t),t), -(u3 + u4)*p(t) + exp(-t*(u1 + u2))), hint='1st_linear_Integral')
In [68]: powsimp(a, deep=True)
Out[68]:
⎛ ⌠ ⎞
⎜ ⎮ ⌠ ⎟ ⌠
⎜ ⎮ -t⋅u₁ - t⋅u₂ + ⎮ (u₃ + u₄) dt ⎟ -⎮ (u₃ + u₄) dt
⎜ ⎮ ⌡ ⎟ ⌡
p(t) = ⎜C₁ + ⎮ ℯ dt⎟⋅ℯ
⎝ ⌡ ⎠
In [69]: %time powsimp(a, deep=True).doit()
CPU times: user 261 ms, sys: 2.11 ms, total: 263 ms
Wall time: 262 ms
Out[69]:
⎛ ⎛⎧ t for u₁ + u₂ - u₃ - u₄ = 0⎞⎞
⎜ ⎜⎪ ⎟⎟
⎜ ⎜⎪ -t⋅u₁ - t⋅u₂ + t⋅(u₃ + u₄) ⎟⎟ -t⋅(u₃ + u₄)
p(t) = ⎜C₁ + ⎜⎨-ℯ ⎟⎟⋅ℯ
⎜ ⎜⎪───────────────────────────── otherwise ⎟⎟
⎜ ⎜⎪ u₁ + u₂ - u₃ - u₄ ⎟⎟
⎝ ⎝⎩ ⎠⎠
一般来说,斯特凡的建议可能适用也可能不适用。理论上,CAS不应该关心你的符号常量是多么复杂,因为它们只是常量。实际上,存在问题,因为它结合了常量,然后需要查看事物是否取消,等等。此外,如上所述的微小差异可能导致实际算法路径的巨大差异。在数学上,两个表达式可以相同,但算法取决于它们在结构上的外观。作为一般经验法则,更简单的表达式往往会做得更好。
如果您发现自己需要使用符号替换子表达式,可以使用cse
来帮助。
假设你的符号是真实的或正面的,一般来说确实有很大帮助,尽管它与这个特殊问题无关。
顺便说一句,SymPy 0.7.3有点老了。最新版本0.7.5刚刚发布。