scipy ode update set_f_params 内部函数设置为 set_solout
scipy ode update set_f_params inside function set as set_solout
将 ode 与 scipy 集成时,ode 接受参数多于 t 和 y 的函数。例如:
def fun(t, y, param1, param2):
并且可以使用 set_f_params
方法设置这些参数的值。
但是,当还使用 set_solout
方法并尝试在此函数内使用 set_f_params
更新参数时,集成保持不变,就好像参数没有被修改一样。
您将如何使用 sol_out 修改参数? 我想受益于 dopri5 密集输出,但我需要在每个时间步更新非齐次项。
下面显示了一个最小示例。
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import ode
def fun(t, x, param):
return x - param
def f_param(t):
return t
ode1 = ode(fun).set_integrator('dopri5').set_initial_value([10.0])
ode1.set_f_params(f_param(0))
results1 = ([], [])
ode2 = ode(fun).set_integrator('dopri5').set_initial_value([10.0])
ode2.set_f_params(f_param(0))
results2 = ([], [])
def callback1(t, x):
results1[0].append(t)
results1[1].append(x.copy())
def callback2(t, x):
results2[0].append(t)
results2[1].append(x.copy())
ode2.set_f_params(f_param(t))
ode1.set_solout(callback1)
ode2.set_solout(callback2)
ode1.integrate(3)
ode2.integrate(3)
plt.plot(results1[0], results1[1], 'o-', alpha=0.7, label='ode1')
plt.plot(results2[0], results2[1], '.--', label='ode2')
plt.legend()
结果如下所示:
受@Wrzlprmft 评论的启发,如果参数不是常量,则不使用参数并直接在要更新的函数中调用更新参数的函数更安全。正如他所说,这意味着:
def fun(t, x):
return x - f_param(t)
但是,该函数(在本例中为 f_param
)可能无法在要集成的函数的命名空间中访问(在上例中为 fun
)。因此,将函数设置为要集成的函数的参数并在开始时使用set_f_params
一次指定函数更方便。
作为问题中代码的延续:
def fun3(t, x, fun):
return -x + fun(t)
def fun4(t, x):
return -x + t
ode3 = ode(fun3).set_integrator('dopri5').set_initial_value([10.0])
ode3.set_f_params(f_param)
results3 = ([], [])
ode4 = ode(fun4).set_integrator('dopri5').set_initial_value([10.0])
results4 = ([], [])
def callback3(t, x):
results3[0].append(t)
results3[1].append(x.copy())
def callback4(t, x):
results4[0].append(t)
results4[1].append(x.copy())
ode3.set_solout(callback3)
ode4.set_solout(callback4)
ode3.integrate(3)
ode4.integrate(3)
plt.figure(figsize=(8, 5))
plt.plot(results1[0], results1[1], 'o-', alpha=0.7, label='ode1')
plt.plot(results2[0], results2[1], '.--', label='ode2')
plt.plot(results3[0], results3[1], 's-.', label='ode3')
plt.plot(results4[0], results4[1], '^-.', label='ode3')
plt.legend()
显示ode3和ode4提供了相同的解决方案:
这就是 the new ODE solvers 在 SciPy 1.0 中发布的方式:
from functools import partial
import numpy as np
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
def fun_fixed(t, x, param):
return x - param
sol_fixed = solve_ivp(
partial(fun_fixed, param=0), (0, 3), [10.0], dense_output=True)
def fun_param(t, x, fun):
return -x + fun(t)
def f_param(t):
return t
sol_param = solve_ivp(
partial(fun_param, fun=f_param), (0, 3), [10.0], dense_output=True)
t = np.linspace(0, 3, num=16)
plt.figure(figsize=(8, 5))
plt.plot(t, sol_fixed.sol(t)[0], 'o-', alpha=0.7, label='ode1')
plt.plot(t, sol_param.sol(t)[0], 's-.', label='ode3')
plt.legend()
将 ode 与 scipy 集成时,ode 接受参数多于 t 和 y 的函数。例如:
def fun(t, y, param1, param2):
并且可以使用 set_f_params
方法设置这些参数的值。
但是,当还使用 set_solout
方法并尝试在此函数内使用 set_f_params
更新参数时,集成保持不变,就好像参数没有被修改一样。
您将如何使用 sol_out 修改参数? 我想受益于 dopri5 密集输出,但我需要在每个时间步更新非齐次项。
下面显示了一个最小示例。
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import ode
def fun(t, x, param):
return x - param
def f_param(t):
return t
ode1 = ode(fun).set_integrator('dopri5').set_initial_value([10.0])
ode1.set_f_params(f_param(0))
results1 = ([], [])
ode2 = ode(fun).set_integrator('dopri5').set_initial_value([10.0])
ode2.set_f_params(f_param(0))
results2 = ([], [])
def callback1(t, x):
results1[0].append(t)
results1[1].append(x.copy())
def callback2(t, x):
results2[0].append(t)
results2[1].append(x.copy())
ode2.set_f_params(f_param(t))
ode1.set_solout(callback1)
ode2.set_solout(callback2)
ode1.integrate(3)
ode2.integrate(3)
plt.plot(results1[0], results1[1], 'o-', alpha=0.7, label='ode1')
plt.plot(results2[0], results2[1], '.--', label='ode2')
plt.legend()
结果如下所示:
受@Wrzlprmft 评论的启发,如果参数不是常量,则不使用参数并直接在要更新的函数中调用更新参数的函数更安全。正如他所说,这意味着:
def fun(t, x):
return x - f_param(t)
但是,该函数(在本例中为 f_param
)可能无法在要集成的函数的命名空间中访问(在上例中为 fun
)。因此,将函数设置为要集成的函数的参数并在开始时使用set_f_params
一次指定函数更方便。
作为问题中代码的延续:
def fun3(t, x, fun):
return -x + fun(t)
def fun4(t, x):
return -x + t
ode3 = ode(fun3).set_integrator('dopri5').set_initial_value([10.0])
ode3.set_f_params(f_param)
results3 = ([], [])
ode4 = ode(fun4).set_integrator('dopri5').set_initial_value([10.0])
results4 = ([], [])
def callback3(t, x):
results3[0].append(t)
results3[1].append(x.copy())
def callback4(t, x):
results4[0].append(t)
results4[1].append(x.copy())
ode3.set_solout(callback3)
ode4.set_solout(callback4)
ode3.integrate(3)
ode4.integrate(3)
plt.figure(figsize=(8, 5))
plt.plot(results1[0], results1[1], 'o-', alpha=0.7, label='ode1')
plt.plot(results2[0], results2[1], '.--', label='ode2')
plt.plot(results3[0], results3[1], 's-.', label='ode3')
plt.plot(results4[0], results4[1], '^-.', label='ode3')
plt.legend()
显示ode3和ode4提供了相同的解决方案:
这就是 the new ODE solvers 在 SciPy 1.0 中发布的方式:
from functools import partial
import numpy as np
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
def fun_fixed(t, x, param):
return x - param
sol_fixed = solve_ivp(
partial(fun_fixed, param=0), (0, 3), [10.0], dense_output=True)
def fun_param(t, x, fun):
return -x + fun(t)
def f_param(t):
return t
sol_param = solve_ivp(
partial(fun_param, fun=f_param), (0, 3), [10.0], dense_output=True)
t = np.linspace(0, 3, num=16)
plt.figure(figsize=(8, 5))
plt.plot(t, sol_fixed.sol(t)[0], 'o-', alpha=0.7, label='ode1')
plt.plot(t, sol_param.sol(t)[0], 's-.', label='ode3')
plt.legend()