使用 scipy.integrate 的 solve_ivp 的意外行为
Unexpected behavior using scipy.integrate's solve_ivp
我想用一个开关函数来集成一个简单的ODE。切换函数将确保对于某些 t 值梯度为零,并使梯度对于任何其他 t 值成为数值常数。
我可以通过使用虚拟状态获得所需的结果,但是当我对单个状态重复计算时,solve_ivp 的输出是不同的。我想明白为什么会这样。
这是重现结果的代码:
#==============================================================================
# import modules
#==============================================================================
from scipy.integrate import solve_ivp
from matplotlib import pyplot as plt
import numpy as np
#==============================================================================
# user defined functions
#==============================================================================
def ode_multi(t, y):
dy1_dt = 0 if t < 3 else 1
dy2_dt = 0 if t < 4 else 1
dy3_dt = dy1_dt - dy2_dt
return [dy1_dt, dy2_dt, dy3_dt]
def ode_single(t, y):
dy1_dt = 0 if t < 3 else 1
dy2_dt = 0 if t < 4 else 1
dy3_dt = dy1_dt - dy2_dt
return [dy3_dt]
#==============================================================================
# solve ode
#==============================================================================
sol_multi = solve_ivp(ode_multi, [0,6], [0,0,0], method='LSODA', t_eval = np.linspace(0,6,100))
sol_single = solve_ivp(ode_single, [0,6], [0], method='LSODA', t_eval = np.linspace(0,6,100))
plt.figure(1)
plt.plot(sol_multi.t, sol_multi.y[0], label='dy1_dt')
plt.plot(sol_multi.t, sol_multi.y[1], label='dy2_dt')
plt.plot(sol_multi.t, sol_multi.y[2], label='dy3_dt')
plt.legend(); plt.grid()
plt.figure(2)
plt.plot(sol_single.t, sol_single.y[0], label='dy3_dt')
plt.legend(); plt.grid()
Ode_multi 输出:
Ode_single输出
我尝试使用 numpy.heaviside 而不是切换函数的 if 语句来确保梯度是可微的。输出是相同的,所以我在代码示例中使用了 if 语句来简化解释。
感谢您的意见!
将步长限制为“奇异”事件之间差异的一半,max_step=0.5
应该适用于这种情况。降低误差容限可以产生类似的效果。
好像积分器跳过了导数不为零的区间。就求解器而言,当内部步长适应导数函数时,就会发生这种情况。现在在初始零段中,求解器假设导数函数是零函数,并相应地增加步长。幸运的是,区间 [3,4]
中的任何一点都没有被评估,所以没有任何东西使假设无效。
第一个示例不是这种情况,因为前两个组件确实发生了变化,因此迫使求解器仔细处理跳跃点。
我想用一个开关函数来集成一个简单的ODE。切换函数将确保对于某些 t 值梯度为零,并使梯度对于任何其他 t 值成为数值常数。
我可以通过使用虚拟状态获得所需的结果,但是当我对单个状态重复计算时,solve_ivp 的输出是不同的。我想明白为什么会这样。
这是重现结果的代码:
#==============================================================================
# import modules
#==============================================================================
from scipy.integrate import solve_ivp
from matplotlib import pyplot as plt
import numpy as np
#==============================================================================
# user defined functions
#==============================================================================
def ode_multi(t, y):
dy1_dt = 0 if t < 3 else 1
dy2_dt = 0 if t < 4 else 1
dy3_dt = dy1_dt - dy2_dt
return [dy1_dt, dy2_dt, dy3_dt]
def ode_single(t, y):
dy1_dt = 0 if t < 3 else 1
dy2_dt = 0 if t < 4 else 1
dy3_dt = dy1_dt - dy2_dt
return [dy3_dt]
#==============================================================================
# solve ode
#==============================================================================
sol_multi = solve_ivp(ode_multi, [0,6], [0,0,0], method='LSODA', t_eval = np.linspace(0,6,100))
sol_single = solve_ivp(ode_single, [0,6], [0], method='LSODA', t_eval = np.linspace(0,6,100))
plt.figure(1)
plt.plot(sol_multi.t, sol_multi.y[0], label='dy1_dt')
plt.plot(sol_multi.t, sol_multi.y[1], label='dy2_dt')
plt.plot(sol_multi.t, sol_multi.y[2], label='dy3_dt')
plt.legend(); plt.grid()
plt.figure(2)
plt.plot(sol_single.t, sol_single.y[0], label='dy3_dt')
plt.legend(); plt.grid()
Ode_multi 输出:
Ode_single输出
我尝试使用 numpy.heaviside 而不是切换函数的 if 语句来确保梯度是可微的。输出是相同的,所以我在代码示例中使用了 if 语句来简化解释。
感谢您的意见!
将步长限制为“奇异”事件之间差异的一半,max_step=0.5
应该适用于这种情况。降低误差容限可以产生类似的效果。
好像积分器跳过了导数不为零的区间。就求解器而言,当内部步长适应导数函数时,就会发生这种情况。现在在初始零段中,求解器假设导数函数是零函数,并相应地增加步长。幸运的是,区间 [3,4]
中的任何一点都没有被评估,所以没有任何东西使假设无效。
第一个示例不是这种情况,因为前两个组件确实发生了变化,因此迫使求解器仔细处理跳跃点。