Matlab ode45 和 Scipy odeint 的区别:相同模型不同结果
Differences between Matlab ode45 and Scipy odeint: same model different results
编辑
所以我能够生成正确的图,但只是在将采样间隔调整为以下内容之后:
t_list = np.linspace(0, 30, 100)
将 X 打印为:
[1. 1. 1. 1. 1. 1.
1. 1. 1. 1. 1. 1.
1. 1. 1. 1. 1. 1.
0.91265299 0.8107059 0.7366542 0.68370578 0.64633005 0.62021062
0.6020953 0.58960093 0.58101775 ...]
但这引出了一个问题:为什么这个系统如此依赖采样间隔?
编辑结束
我正在尝试使用 scipy 将一个简单的 matlab 微分方程系统重建为 python。我不知道为什么我在 scipy 执行中收到 RuntimeWarning: invalid value encountered in double_scalars
。我是否在 odeint 调用中缺少可选参数?
python:
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
def model(y0, t):
x = y0[0]
y = y0[1]
z = y0[2]
if t <= 10:
sys_input = 1.0
else:
sys_input = 0.75
a = 1.0
b = 1.0
c = 1.0
E = 1.0
dxdt = sys_input - a * E * (x ** 0.5)
dydt = a * E * (x ** 0.5) - b * (y ** 0.5)
dzdt = b * (y ** 0.5) - c * (z ** 0.5)
return [dxdt, dydt, dzdt]
t_list = np.linspace(0, 30, 31)
# Initial conditions vector
yi = [1.0, 1.0, 1.0]
ret = odeint(model, y0=yi, t=t_list)
X = ret[:, 0]
print(X)
并打印以下内容:
<input>:18: RuntimeWarning: invalid value encountered in double_scalars
<input>:19: RuntimeWarning: invalid value encountered in double_scalars
[ 1. 1. 1. 1. nan nan nan nan nan nan nan nan nan nan nan nan nan nan
nan nan nan nan nan nan nan nan nan nan nan nan nan]
下面的 matlab 代码会产生一个连续的结果:
tspan = [0,30];
x0 = 1.0; % Initial value of x
y0 = 1.0; % Initial value of y
z0 = 1.0; % Initial value of z
initial_values = [x0; y0; z0]; % Initial value of the vector w
[T,R] = ode45(@(t,w) diff_eq(t,w),tspan,initial_values);
X = R(:,1);
Y = R(:,2);
Z = R(:,3);
for i = 1: length(X)
if(mod(i, 10)==0 && i > 1)
disp(' ');
end
fprintf('X[%i] = %.2f, ', i, X(i));
end
disp(' ');
function dw_vectordt = diff_eq(t,w_vector)
x = w_vector(1);
y = w_vector(2);
z = w_vector(3);
if (t<=10)
sys_input= 1.0;
else
sys_input=0.75;
end
a = 1.0;
b = 1.0;
c = 1.0;
E = 1.0;
dxdt = sys_input-a*E*x^(0.5);
dydt = a*E*x^(0.5)-b*y^(0.5);
dzdt = b*y^(0.5)-c*z^(0.5);
dw_vectordt = [dxdt; dydt; dzdt];
end
打印语句returns:
X[1] = 1.00, X[2] = 1.00, X[3] = 1.00, X[4] = 1.00, X[5] = 1.00, X[6] = 1.00, X[7] = 1.00, X[8] = 1.00, X[9] = 1.00,
X[10] = 1.00, X[11] = 1.00, X[12] = 1.00, X[13] = 1.00, X[14] = 1.01, X[15] = 0.99, X[16] = 0.92, X[17] = 0.82, X[18] = 0.78, X[19] = 0.74,
X[20] = 0.71, X[21] = 0.68, X[22] = 0.66, X[23] = 0.64, X[24] = 0.63, X[25] = 0.61, X[26] = 0.60, X[27] = 0.59, X[28] = 0.59, X[29] = 0.58,
X[30] = 0.58, X[31] = 0.57, X[32] = 0.57, X[33] = 0.57, X[34] = 0.57, X[35] = 0.57, X[36] = 0.56, X[37] = 0.56, X[38] = 0.56, X[39] = 0.56,
X[40] = 0.56, X[41] = 0.56, X[42] = 0.56, X[43] = 0.56, X[44] = 0.56, X[45] = 0.56, X[46] = 0.56, X[47] = 0.56, X[48] = 0.56, X[49] = 0.56,
X[50] = 0.56, X[51] = 0.56, X[52] = 0.56, X[53] = 0.56,
您正在处理的 ODE 系统可能很僵硬。您遇到的 RuntimeWarning
是由平方根运算引起的,因为 y0
的元素在积分期间变为负数。发生这种情况是因为积分器的时间步长太大,并且您使用的求解器不太适合潜在的刚性系统。将提供给 t
的元素数量增加到 t_list
可能会减少时间步长,因此可以使用变通方法。为了更好地理解正在发生的事情,我鼓励您使用下面的代码片段,它使用 SciPy 推荐的较新的 solve_ivp
API。特别感兴趣的是关键字参数 method
和 max_step
。使用 RK23
、RK45
、DOP853
和 LSODA
中的任何一个作为方法将导致开箱即用的不良解估计,因为所有这些求解器都以非刚性开始方法。 LSODA 应该检测到刚度并切换到刚性积分器,但是随着时间步长的快速增加,它没有这样做。对于所有这些方法,设置 max_step=0.5
允许以计算时间的潜在成本处理 ODE 系统。或者,使用 Radau
或 BDF
将开箱即用,因为这些求解器可以处理刚性 ODE 系统。不过建议手动提供系统的雅可比行列式,否则用有限差分逼近
import numpy as np
from scipy.integrate import solve_ivp
def model(t, y0):
x, y, z = y0
sys_input = 1 if t <= 10 else 0.75
a, b, c, E = 1, 1, 1, 1
return (sys_input - a * E * np.sqrt(x),
a * E * np.sqrt(x) - b * np.sqrt(y),
b * np.sqrt(y) - c * np.sqrt(z))
sol = solve_ivp(model, t_span=(0, 30), y0=(1, 1, 1))
print(sol)
编辑
所以我能够生成正确的图,但只是在将采样间隔调整为以下内容之后:
t_list = np.linspace(0, 30, 100)
将 X 打印为:
[1. 1. 1. 1. 1. 1.
1. 1. 1. 1. 1. 1.
1. 1. 1. 1. 1. 1.
0.91265299 0.8107059 0.7366542 0.68370578 0.64633005 0.62021062
0.6020953 0.58960093 0.58101775 ...]
但这引出了一个问题:为什么这个系统如此依赖采样间隔?
编辑结束
我正在尝试使用 scipy 将一个简单的 matlab 微分方程系统重建为 python。我不知道为什么我在 scipy 执行中收到 RuntimeWarning: invalid value encountered in double_scalars
。我是否在 odeint 调用中缺少可选参数?
python:
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
def model(y0, t):
x = y0[0]
y = y0[1]
z = y0[2]
if t <= 10:
sys_input = 1.0
else:
sys_input = 0.75
a = 1.0
b = 1.0
c = 1.0
E = 1.0
dxdt = sys_input - a * E * (x ** 0.5)
dydt = a * E * (x ** 0.5) - b * (y ** 0.5)
dzdt = b * (y ** 0.5) - c * (z ** 0.5)
return [dxdt, dydt, dzdt]
t_list = np.linspace(0, 30, 31)
# Initial conditions vector
yi = [1.0, 1.0, 1.0]
ret = odeint(model, y0=yi, t=t_list)
X = ret[:, 0]
print(X)
并打印以下内容:
<input>:18: RuntimeWarning: invalid value encountered in double_scalars
<input>:19: RuntimeWarning: invalid value encountered in double_scalars
[ 1. 1. 1. 1. nan nan nan nan nan nan nan nan nan nan nan nan nan nan
nan nan nan nan nan nan nan nan nan nan nan nan nan]
下面的 matlab 代码会产生一个连续的结果:
tspan = [0,30];
x0 = 1.0; % Initial value of x
y0 = 1.0; % Initial value of y
z0 = 1.0; % Initial value of z
initial_values = [x0; y0; z0]; % Initial value of the vector w
[T,R] = ode45(@(t,w) diff_eq(t,w),tspan,initial_values);
X = R(:,1);
Y = R(:,2);
Z = R(:,3);
for i = 1: length(X)
if(mod(i, 10)==0 && i > 1)
disp(' ');
end
fprintf('X[%i] = %.2f, ', i, X(i));
end
disp(' ');
function dw_vectordt = diff_eq(t,w_vector)
x = w_vector(1);
y = w_vector(2);
z = w_vector(3);
if (t<=10)
sys_input= 1.0;
else
sys_input=0.75;
end
a = 1.0;
b = 1.0;
c = 1.0;
E = 1.0;
dxdt = sys_input-a*E*x^(0.5);
dydt = a*E*x^(0.5)-b*y^(0.5);
dzdt = b*y^(0.5)-c*z^(0.5);
dw_vectordt = [dxdt; dydt; dzdt];
end
打印语句returns:
X[1] = 1.00, X[2] = 1.00, X[3] = 1.00, X[4] = 1.00, X[5] = 1.00, X[6] = 1.00, X[7] = 1.00, X[8] = 1.00, X[9] = 1.00,
X[10] = 1.00, X[11] = 1.00, X[12] = 1.00, X[13] = 1.00, X[14] = 1.01, X[15] = 0.99, X[16] = 0.92, X[17] = 0.82, X[18] = 0.78, X[19] = 0.74,
X[20] = 0.71, X[21] = 0.68, X[22] = 0.66, X[23] = 0.64, X[24] = 0.63, X[25] = 0.61, X[26] = 0.60, X[27] = 0.59, X[28] = 0.59, X[29] = 0.58,
X[30] = 0.58, X[31] = 0.57, X[32] = 0.57, X[33] = 0.57, X[34] = 0.57, X[35] = 0.57, X[36] = 0.56, X[37] = 0.56, X[38] = 0.56, X[39] = 0.56,
X[40] = 0.56, X[41] = 0.56, X[42] = 0.56, X[43] = 0.56, X[44] = 0.56, X[45] = 0.56, X[46] = 0.56, X[47] = 0.56, X[48] = 0.56, X[49] = 0.56,
X[50] = 0.56, X[51] = 0.56, X[52] = 0.56, X[53] = 0.56,
您正在处理的 ODE 系统可能很僵硬。您遇到的 RuntimeWarning
是由平方根运算引起的,因为 y0
的元素在积分期间变为负数。发生这种情况是因为积分器的时间步长太大,并且您使用的求解器不太适合潜在的刚性系统。将提供给 t
的元素数量增加到 t_list
可能会减少时间步长,因此可以使用变通方法。为了更好地理解正在发生的事情,我鼓励您使用下面的代码片段,它使用 SciPy 推荐的较新的 solve_ivp
API。特别感兴趣的是关键字参数 method
和 max_step
。使用 RK23
、RK45
、DOP853
和 LSODA
中的任何一个作为方法将导致开箱即用的不良解估计,因为所有这些求解器都以非刚性开始方法。 LSODA 应该检测到刚度并切换到刚性积分器,但是随着时间步长的快速增加,它没有这样做。对于所有这些方法,设置 max_step=0.5
允许以计算时间的潜在成本处理 ODE 系统。或者,使用 Radau
或 BDF
将开箱即用,因为这些求解器可以处理刚性 ODE 系统。不过建议手动提供系统的雅可比行列式,否则用有限差分逼近
import numpy as np
from scipy.integrate import solve_ivp
def model(t, y0):
x, y, z = y0
sys_input = 1 if t <= 10 else 0.75
a, b, c, E = 1, 1, 1, 1
return (sys_input - a * E * np.sqrt(x),
a * E * np.sqrt(x) - b * np.sqrt(y),
b * np.sqrt(y) - c * np.sqrt(z))
sol = solve_ivp(model, t_span=(0, 30), y0=(1, 1, 1))
print(sol)