Python Mathematica';s NDSolve和SciPy';s solve_ivp返回不同的结果
这个问题涉及Python和Mathematica代码 我想用数值方法解一个复杂的常微分方程组。最初,我使用NDSolve,但由于最终需要进行广泛的参数搜索,我决定改用Python,特别是SciPy的solve_ivp函数。但是,我很快遇到了以下问题: A、 B、C和D是我想要求解的四个耦合复常微分方程。值得注意的是,abs(A)^2+abs(B)^2+abs(C)^2+abs(D)^2=1始终有效(由于问题的性质)。事实上,这在NDSolve的解决方案中始终是正确的 但是,当我将代码传输到Python时,使用solve_ivp的结果是不正确的 起初,当我把代码转移到Mathematica时,我确信有一个输入错误。然而,我花了几天时间查看了这些函数,没有发现任何东西。我还让一位熟悉这两种语言的朋友查看了一遍,他们也没有发现任何东西(但那是一个稍微不同的代码版本,我简化了它以使其更具可读性) 因此,我的问题是,NDSolve或solve_ivp的定义中是否有可能导致这种差异,或者是否有一种方法可以在使用Python时重现Mathematica的结果 Mathematica代码:Python Mathematica';s NDSolve和SciPy';s solve_ivp返回不同的结果,python,scipy,wolfram-mathematica,ode,Python,Scipy,Wolfram Mathematica,Ode,这个问题涉及Python和Mathematica代码 我想用数值方法解一个复杂的常微分方程组。最初,我使用NDSolve,但由于最终需要进行广泛的参数搜索,我决定改用Python,特别是SciPy的solve_ivp函数。但是,我很快遇到了以下问题: A、 B、C和D是我想要求解的四个耦合复常微分方程。值得注意的是,abs(A)^2+abs(B)^2+abs(C)^2+abs(D)^2=1始终有效(由于问题的性质)。事实上,这在NDSolve的解决方案中始终是正确的 但是,当我将代码传输到Py
r[t_, c_, b_] := Sqrt[(c*t)^2 + b^2]
Sol[t0_, tf_, C1_, C2_, a_, c_, b_, d_, \[Phi]1_, \[Phi]2_] :=
Module[{g}, g = C1*C2 ;
NDSolve[{A'[
t] == -I (A[
t]*(C1*a/2 + C2*a*UnitStep[d - r[t, c, b]]/2 +
g/r[t, c, b]^3) -
3*g/r[t, c, b]^5*C11[t]*((c*t)^2 - b^2 - 2*I*c*t*b)),
A[t0] == Cos[\[Phi]1]/Sqrt[2],
B'[t] == -I*(D1[
t]*(2*g/r[t, c, b]^3 - 3*g/r[t, c, b]^5*((c*t)^2 + b^2)) +
B[t]*(-g/r[t, c, b]^3 + C1*a/2 -
C2*a*UnitStep[d - r[t, c, b]]/2)),
B[t0] == Sin[\[Phi]1]/Sqrt[2],
C11'[t] ==
-I*(C11[
t]*(-C1*a/2 - C2*a*UnitStep[d - r[t, c, b]]/2 +
g/r[t, c, b]^3) -
3*g/r[t, c, b]^5*A[t]*((c*t)^2 - b^2 + 2*I*c*t*b)),
C11[t0] == Sin[\[Phi]1]*E^(-I*\[Phi]2)/Sqrt[2], D1'[t] ==
-I*(B[t]*(2*g/r[t, c, b]^3 - 3*g/r[t, c, b]^5*((c*t)^2 + b^2)) +
D1[t]*(-g/r[t, c, b]^3 - C1*a/2 +
C2*a*UnitStep[d - r[t, c, b]]/2)),
D1[t0] == Cos[\[Phi]1]*E^(-I*\[Phi]2)/Sqrt[2]}, {A, B, C11,
D1}, {t, t0, tf}]]
要复制结果,请执行以下操作:
S = Sol[-100, 100, 1, 1.25, 100, 1, 1, 10, 0, 0];
Rasterize[
Plot[{Abs[A[t]]^2 /. S, Abs[B[t]]^2 /. S, Abs[C11[t]]^2 /. S,
Abs[D1[t]]^2 /. S,
Abs[A[t]]^2 + Abs[B[t]]^2 + Abs[C11[t]]^2 + Abs[D1[t]]^2 /.
S}, {t, -100, 100},
PlotLegends -> {"A^2", "B^2", "C^2", "D^2", "Sum=1"},
PlotLabel -> "Working Version with Mathematica NDSolve"]]
k = [1, 1.25, 100, 1, 1, 10]
sol=solver(-100, 100, k, 0, 0)
plt.plot(sol.t, np.abs(sol.y[0])**2, label="A^2")
plt.plot(sol.t, np.abs(sol.y[1])**2, label="B^2")
plt.plot(sol.t, np.abs(sol.y[2])**2, label = "C^2")
plt.plot(sol.t, np.abs(sol.y[3])**2, label="D^2")
plt.plot(sol.t, np.abs(sol.y[0])**2+np.abs(sol.y[1])**2+np.abs(sol.y[2])**2+np.abs(sol.y[3])**2, label="Sum != 1???")
plt.legend()
plt.title("Not Working solve_ivp Version")
plt.show()
Python代码:
from scipy.integrate import solve_ivp
import numpy as np
import matplotlib.pyplot as plt
def r(t, c, b):
return np.sqrt((c*t)**2 + b**2)
def coeff(t, z, k):
C1, C2, a, c, b, d = k
g = C1*C2
A, B, C, D = z
dAdt = -1j * (A * (C1*a/2 + C2*a*np.heaviside(d-r(t,c,b), 1)/2 +
g/r(t,c,b)**3) - 3*g/r(t,c,b)**5 * C * ((c * t)**2 - b**2 - 2*1j*c*t*b))
dBdt = -1j*(D*(2*g/r(t,c,b)**3 - 3*g/r(t,c,b)**5*((c*t)**2 + b**2)) + B *(-g/r(t,c,b)**3 + C1*a/2 - C2*a*np.heaviside(d-r(t,c,b), 1)/2))
dCdt = -1j * (C*(-C1*a/2 - C2*a*np.heaviside(d-r(t,c,b), 1)/2 +
g/r(t,c,b)**3) - 3*g/r(t,c,b)**5*A*((c*t)**2 - b**2 + 2*1j*c*t*b))
dDdt = -1j*(B*(2*g/r(t,c,b)**3- 3*g/r(t,c,b)**5*((c*t)**2 + b**2)) + D *(-g/r(t,c,b)**3 - C1*a/2 + C2*a*np.heaviside(d-r(t,c,b), 1)/2))
return [dAdt, dBdt, dCdt, dDdt]
def solver(t0, tf, k, phi1, phi2):
a0 = np.cos(phi1)/np.sqrt(2)
b0 = np.sin(phi1)/np.sqrt(2)
c0 = (np.sin(phi1)/np.sqrt(2))*np.exp(-1j*phi2)
d0 = (np.cos(phi1)/np.sqrt(2))*np.exp(-1j*phi2)
z0 = (a0, b0, c0, d0)
return solve_ivp(coeff, [t0, tf], z0, args=(k,))
要复制结果,请执行以下操作:
S = Sol[-100, 100, 1, 1.25, 100, 1, 1, 10, 0, 0];
Rasterize[
Plot[{Abs[A[t]]^2 /. S, Abs[B[t]]^2 /. S, Abs[C11[t]]^2 /. S,
Abs[D1[t]]^2 /. S,
Abs[A[t]]^2 + Abs[B[t]]^2 + Abs[C11[t]]^2 + Abs[D1[t]]^2 /.
S}, {t, -100, 100},
PlotLegends -> {"A^2", "B^2", "C^2", "D^2", "Sum=1"},
PlotLabel -> "Working Version with Mathematica NDSolve"]]
k = [1, 1.25, 100, 1, 1, 10]
sol=solver(-100, 100, k, 0, 0)
plt.plot(sol.t, np.abs(sol.y[0])**2, label="A^2")
plt.plot(sol.t, np.abs(sol.y[1])**2, label="B^2")
plt.plot(sol.t, np.abs(sol.y[2])**2, label = "C^2")
plt.plot(sol.t, np.abs(sol.y[3])**2, label="D^2")
plt.plot(sol.t, np.abs(sol.y[0])**2+np.abs(sol.y[1])**2+np.abs(sol.y[2])**2+np.abs(sol.y[3])**2, label="Sum != 1???")
plt.legend()
plt.title("Not Working solve_ivp Version")
plt.show()
编辑:
我试着拆分ODE,以便消除由于Heaviside函数造成的不连续性。它确实让事情变得更好了,但并没有太多:我认为最有趣的是t=0时的偏移,因为这不是一个间断。即使在这一点之前,函数也会像以前一样从Sum=1开始移动
新代码:
from scipy.integrate import solve_ivp
import numpy as np
import matplotlib.pyplot as plt
def r(t, c, b):
return np.sqrt((c*t)**2 + b**2)
def coeff(t, z, k):
C1, C2, a, c, b, d = k
g = C1*C2
A, B, C, D = z
dAdt = -1j * (A * (C1*a/2 + C2*a/2 +
g/r(t,c,b)**3) - 3*g/r(t,c,b)**5 * C * ((c * t)**2 - b**2 - 2*1j*c*t*b))
dBdt = -1j*(D*(2*g/r(t,c,b)**3 - 3*g/r(t,c,b)**5*((c*t)**2 + b**2)) + B *(-g/r(t,c,b)**3 + C1*a/2 - C2*a/2))
dCdt = -1j * (C*(-C1*a/2 - C2*a/2 +
g/r(t,c,b)**3) - 3*g/r(t,c,b)**5*A*((c*t)**2 - b**2 + 2*1j*c*t*b))
dDdt = -1j*(B*(2*g/r(t,c,b)**3- 3*g/r(t,c,b)**5*((c*t)**2 + b**2)) + D *(-g/r(t,c,b)**3 - C1*a/2 + C2*a))
return [dAdt, dBdt, dCdt, dDdt]
def coeff2(t, z, k):
C1, C2, a, c, b, d = k
g = C1*C2
A, B, C, D = z
dAdt = -1j * (A * (C1*a/2 +
g/r(t,c,b)**3) - 3*g/r(t,c,b)**5 * C * ((c * t)**2 - b**2 - 2*1j*c*t*b))
dBdt = -1j*(D*(2*g/r(t,c,b)**3 - 3*g/r(t,c,b)**5*((c*t)**2 + b**2)) + B *(-g/r(t,c,b)**3 + C1*a/2))
dCdt = -1j * (C*(-C1*a/2 +
g/r(t,c,b)**3) - 3*g/r(t,c,b)**5*A*((c*t)**2 - b**2 + 2*1j*c*t*b))
dDdt = -1j*(B*(2*g/r(t,c,b)**3- 3*g/r(t,c,b)**5*((c*t)**2 + b**2)) + D *(-g/r(t,c,b)**3 - C1*a/2))
return [dAdt, dBdt, dCdt, dDdt]
def solver(t0, tf, k, phi1, phi2, order=1):
a0 = np.cos(phi1)/np.sqrt(2)
b0 = np.sin(phi1)/np.sqrt(2)
c0 = (np.sin(phi1)/np.sqrt(2))*np.exp(-1j*phi2)
d0 = (np.cos(phi1)/np.sqrt(2))*np.exp(-1j*phi2)
z0 = (a0, b0, c0, d0)
if order == 2 :
return solve_ivp(coeff, [t0, tf], z0, args=(k,))
else:
return solve_ivp(coeff2, [t0, tf], z0, args=(k,))
k = [1, 1.25, 100, 1, 1, 10]
t0=-100
tf=100
t1 = -np.sqrt(k[-1]**2 - k[-2]**2)/k[-4] - 0.00000001
t2 = np.sqrt(k[-1]**2 - k[-2]**2)/k[-4] + 0.000000001
sol1 = solver(t0, t1, k, 0, 0)
sol2 = solve_ivp(coeff, [t1,t2], [sol1.y[0][-1], sol1.y[1][-1], sol1.y[2][-1], sol1.y[3][-1]], args=(k,))
sol3 = solve_ivp(coeff2, [t2,tf], [sol2.y[0][-1], sol2.y[1][-1], sol2.y[2][-1], sol2.y[3][-1]], args=(k,))
t = np.concatenate((sol1.t,sol2.t, sol3.t), axis=None)
A = np.concatenate((sol1.y[0],sol2.y[0],sol3.y[0]), axis=None)
B = np.concatenate((sol1.y[1],sol2.y[1],sol3.y[1]), axis=None)
C = np.concatenate((sol1.y[2],sol2.y[2],sol3.y[2]), axis=None)
D = np.concatenate((sol1.y[3],sol2.y[3],sol3.y[3]), axis=None)
plt.plot(t, np.abs(A)**2, label="A^2")
plt.plot(t, np.abs(B)**2, label="B^2")
plt.plot(t, np.abs(C)**2, label = "C^2")
plt.plot(t, np.abs(D)**2, label="D^2")
plt.plot(t, np.abs(A)**2+np.abs(B)**2+np.abs(C)**2+np.abs(D)**2, label="Sum != 1???")
plt.legend()
plt.title("Not Working solve_ivp Version")
print(np.max(np.abs(A)**2+np.abs(B)**2+np.abs(C)**2+np.abs(D)**2))
plt.show()
Mathematica足够聪明,能够找出哪种类型的解算器在大多数情况下最精确;使用
scipy.integrate.solve_ivp
时,如果默认设置不够好,则必须执行此操作。不幸的是,并非所有的solve_ivp
解算器都能很好地处理复杂值问题,而其他支持复杂值的解算器在ODE中的表现要比默认解算器糟糕得多(RK45
)
你会发现的
你的导数包括np.heaviside
函数,它们是不连续的;这是ODE解算器通常不喜欢的东西;例如,RK45设计用于可由具有连续四阶导数的函数局部近似的函数。考虑一下衍生品的跳跃在哪里,并在它们之间分段。例如,如果要在范围t=0..1上求解,且不连续性在t=a和t=b处;然后从0积分到a-1e-9,再从a+1e-9积分到b-1e-9,再从b+1e-9积分到1。坏解的图表明,导数在几个离散点上存在问题
编辑:仔细检查后,系数
1/r(t,c,b)**3
和**5
在t=0时急剧达到峰值(增加几个数量级);可能是颂歌在那里变得“僵硬”。(像d(A,B)/dt=(-aB,-bA)和正A,B的无害ODE倾向于僵硬)。将变量拆分为实部和虚部,以便您可以使用适用于实值刚性方程的解算器可能值得一试。Hm,如果我将问题重新定义为8个实数常微分方程而不是4个复数常微分方程,您会推荐另一个解算器吗?我完全可以这样做,并利用你的建议来处理不连续性。我也关注了你的链接,但与这里给出的示例不同,Trace[]的结果只显示“自动”。谢谢!我试图按照你的建议消除不连续性,虽然情节看起来更好,但答案仍然不正确(见编辑后的文章)。我想我会把它改写成一套真正的颂歌。