Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/webpack/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python Mathematica';s NDSolve和SciPy';s solve_ivp返回不同的结果_Python_Scipy_Wolfram Mathematica_Ode - Fatal编程技术网

Python Mathematica';s NDSolve和SciPy';s solve_ivp返回不同的结果

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

这个问题涉及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代码:

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[]的结果只显示“自动”。谢谢!我试图按照你的建议消除不连续性,虽然情节看起来更好,但答案仍然不正确(见编辑后的文章)。我想我会把它改写成一套真正的颂歌。