我尝试使用四阶龙格-库塔法来近似一阶常微分方程系统的解。我认为RK 4实现本身是正确的,如果有点不正确-它产生的图形看起来像正确的形状无论如何-但它依赖于3个常数 c,d 和 h。我想看看当我改变这些常数时,解是如何变化的。我 * 可以 * 手动改变它们,但是我想用滑块使它具有交互性。
我希望最终有一个滑块的每一个3常数;现在,我甚至不能让一个滑动条(c 的滑动条)正常工作,它肯定是...... present,我可以来回滑动它,但是图形不会随着 c 值的变化而更新--尽管我已经为它定义了一个Slider.on_changed
,并且记得用 slider 值重新调用RK 4函数并重新设置线数据:
%matplotlib notebook
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button
import math
y_inits = [95, 5, 0]
c = 1
d = 5
fStepSize = 0.01
iLower = 0
iUpper = 1
tDerivatives = [
lambda t, y1, y2, y3: -c*y1*y2,
lambda t, y1, y2, y3: (c*y1*y2) - (d*y2),
lambda t, y1, y2, y3: d*y2
]
tColors = ["blue", "red", "yellow", "green", "black", "purple"]
def RK4(c, d, fStepSize):
tY_est = [ [y_inits[i]] for i in range(len(tDerivatives)) ]
tT = [iLower]
iRange = iUpper - iLower
n = math.ceil(iRange / fStepSize)
for i in range(n+1)[1:]:
fT_last = tT[i-1]
fT_new = fT_last + fStepSize
tK = []
for j in range(len(tDerivatives)):
Derivative = tDerivatives[j]
fK1 = fStepSize * Derivative(fT_last, *[tY_est[k][i-1] for k in range(len(tY_est))])
tK.append(["y'"+str(j+1), fK1])
for j in range(len(tDerivatives)):
Derivative = tDerivatives[j]
fK2 = fStepSize * Derivative(fT_last + (fStepSize/2), *[tY_est[k][i-1] + (tK[k][1]/2) for k in range(len(tY_est))])
tK[j].append(fK2)
for j in range(len(tDerivatives)):
Derivative = tDerivatives[j]
fK3 = fStepSize * Derivative(fT_last + (fStepSize/2), *[tY_est[k][i-1] + (tK[k][2]/2) for k in range(len(tY_est))])
tK[j].append(fK3)
for j in range(len(tDerivatives)):
Derivative = tDerivatives[j]
fK4 = fStepSize * Derivative(fT_new, *[tY_est[k][i-1] + tK[k][3] for k in range(len(tY_est))])
tK[j].append(fK4)
for j in range(len(tY_est)):
fY_est_new = tY_est[j][i-1] + (( tK[j][1] + (2*tK[j][2]) + (2*tK[j][3]) + tK[j][4] )/6)
tY_est[j].append(fY_est_new)
tT.append(fT_new)
return tT, tY_est
fig, ax = plt.subplots()
plt.subplots_adjust(left=0.25, bottom=0.25)
tT_init, tY_est_init = RK4(c, d, fStepSize)
tPlots = [ ax.plot(tT_init, tY_est_init[i], marker=".", color=tColors[i]) for i in range(len(tDerivatives)) ]
plt.xlabel('t')
plt.ylabel('y(t)')
plt.title('h = '+str(fStepSize))
axfreq = fig.add_axes([0.25, 0.1, 0.65, 0.03])
c_slider = Slider(
ax=axfreq,
label='c',
valmin=0.1,
valmax=30,
valinit=c,
)
def Update(val):
tT, tY_est = RK4(c_slider.val, d, fStepSize)
for i in range(len(tPlots)):
tPlots[i].set_data(tT, tY_est[i])
fig.canvas.draw()
c_slider.on_changed(Update)
不会引发任何错误消息。
我应该指出,我使用this slider demo from the matplotlib site itself作为我的滑块实现的基础。* 他们的 * 代码工作得很好,即使在我的环境中,最终的绘图线也会响应滑块的变化-所以这不仅仅是“Matplotlib在Jupyter Notebook中没有交互”的问题。
我也尝试过修改Update
中的fig.canvas.draw()
行--也许它应该是draw_idle
,或者也许我应该使用plt.draw()
,或者其他一些东西,但是它们似乎都没有任何效果。
我需要做哪些更改才能使滑块响应?
**EDIT:**正如Yacine答案的注解中所讨论的,结果有两个问题。除了如何示例化这些行(参见Yacine的答案),另一个问题是上面的代码定义RK 4实际上没有将c_slider.val
传递给需要它们的RK 4子例程。因此,图形 * 正在 * 更新......但每次都使用完全相同的全局 c 变量。
完整的解决方案,与所有3滑块我想要的,如下所示:
%matplotlib notebook
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button
import math
y_inits = [95, 5, 0]
c_init = 1
d_init = 5
fStepSize = 0.01
iLower = 0
iUpper = 1
tDerivatives = [
lambda c, d, t, y1, y2, y3: -c*y1*y2,
lambda c, d, t, y1, y2, y3: (c*y1*y2) - (d*y2),
lambda c, d, t, y1, y2, y3: d*y2
]
tColors = ["blue", "red", "yellow", "green", "black", "purple"]
def RK4(c, d, fStepSize):
tY_est = [ [y_inits[i]] for i in range(len(tDerivatives)) ]
tT = [iLower]
iRange = iUpper - iLower
n = math.ceil(iRange / fStepSize)
for i in range(n+1)[1:]:
fT_last = tT[i-1]
fT_new = fT_last + fStepSize
tK = []
for j in range(len(tDerivatives)):
Derivative = tDerivatives[j]
fK1 = fStepSize * Derivative(c, d, fT_last, *[tY_est[k][i-1] for k in range(len(tY_est))])
tK.append(["y'"+str(j+1), fK1])
for j in range(len(tDerivatives)):
Derivative = tDerivatives[j]
fK2 = fStepSize * Derivative(c, d, fT_last + (fStepSize/2), *[tY_est[k][i-1] + (tK[k][1]/2) for k in range(len(tY_est))])
tK[j].append(fK2)
for j in range(len(tDerivatives)):
Derivative = tDerivatives[j]
fK3 = fStepSize * Derivative(c, d, fT_last + (fStepSize/2), *[tY_est[k][i-1] + (tK[k][2]/2) for k in range(len(tY_est))])
tK[j].append(fK3)
for j in range(len(tDerivatives)):
Derivative = tDerivatives[j]
fK4 = fStepSize * Derivative(c, d, fT_new, *[tY_est[k][i-1] + tK[k][3] for k in range(len(tY_est))])
tK[j].append(fK4)
for j in range(len(tY_est)):
fY_est_new = tY_est[j][i-1] + (( tK[j][1] + (2*tK[j][2]) + (2*tK[j][3]) + tK[j][4] )/6)
tY_est[j].append(fY_est_new)
tT.append(fT_new)
return tT, tY_est
fig, ax = plt.subplots()
plt.subplots_adjust(left=0.25, bottom=0.25)
plt.xlabel('t')
plt.ylabel('y(t)')
plt.title('h = '+str(fStepSize))
c_slider = Slider(
ax=fig.add_axes([0.25, 0.1, 0.65, 0.03]),
label='c',
valmin=0.1,
valmax=5,
valinit=c_init,
)
d_slider = Slider(
ax=fig.add_axes([0.25, 0.06, 0.65, 0.03]),
label='d',
valmin=0.1,
valmax=10,
valinit=d_init,
)
h_slider = Slider(
ax=fig.add_axes([0.25, 0.02, 0.65, 0.03]),
label='h',
valmin=0.001,
valmax=0.1,
valinit=fStepSize,
)
tPlots = [ax.plot([], [], marker=".", color=tColors[i])[0] for i in range(len(tDerivatives))]
def Update(val):
fStepSize = h_slider.val
tT, tY_est = RK4(c_slider.val, d_slider.val, fStepSize)
for i in range(len(tPlots)):
tPlots[i].set_data(tT, tY_est[i])
fig.canvas.draw_idle()
ax.relim()
ax.autoscale_view()
c_slider.on_changed(Update)
d_slider.on_changed(Update)
h_slider.on_changed(Update)
tT_init, tY_est_init = RK4(c_init, d_init, fStepSize)
for i in range(len(tPlots)):
tPlots[i].set_data(tT_init, tY_est_init[i])
ax.relim()
ax.autoscale_view()
plt.show()
2条答案
按热度按时间7uzetpgm1#
我认为问题在于你需要在更新函数中设置线的数据。你在RK4函数中设置初始图的数据,但是当滑块值改变时,它不会更新图。
hmmo2u0o2#
还有一个选项是使用ipywidgets的interactive,让它处理与滑块的连接和响应,当提供所需值的元组时,它实际上会自己创建滑块,就像我下面提到的例子一样;但是,由于您需要初始设置,我在此处定义了滑块。
应该是这样的但是,我在将所有内容组合在一起以正确显示三行时遇到了一个小问题。**因此,现在,这只是演示了滑块处理,无法正确绘制(?!?!)。**它至少可以响应滑块的更改:
这基于here和here。
如果ipywidgets的
interactive
真的有同样的情节,它的销售会更好,但也许最终我会看到这个问题并解决它。