使用 Numba 求解 ODE
Solve ODEs with Numba
我正在尝试使用 Numba 加快我的 ODE 求解器的速度,但以下代码会引发键入错误:
import numpy as np
import matplotlib.pyplot as plt
from numba import njit
@njit
def pend(t, y, b, c):
theta, omega = y
dydt = np.array([omega, -b*omega - c*np.sin(theta)])
return dydt
@njit
def rungeStep(f, t, y0, tau, params):
k1 = tau * f(t, y0, *params)
k2 = tau * f(t, y0 + k1 / 2, *params)
k3 = tau * f(t, y0 + k2 / 2, *params)
k4 = tau * f(t, y0 + k3, *params)
return (k1 + 2 * k2 + 2 * k3 + k4) / 6
@njit
def integrate(f, t0, y0, tEnd, h, params):
ys = y0.copy()
t = np.array(t0)
while t0 <= tEnd:
y0 += rungeStep(f, t0, y0[0], h, params)
t0 += h
ys = np.concatenate((ys, y0), axis=0)
t = np.append(t, t0)
return t, ys.T
args = (0.25, 5)
y0 = np.array([[np.pi - 0.1, 0.0]])
t, y = integrate(pend, 0, y0, 10, 1, args)
这导致:
TypingError: Failed in nopython mode pipeline (step: nopython frontend)
Cannot unify array(int64, 0d, C) and array(int64, 1d, C) for 't.2', defined at <ipython-input-56-38d2ea70b889> (6)
File "<ipython-input-56-38d2ea70b889>", line 6:
def inagrate(f, t0, y0, tEnd, h, params):
<source elided>
while t0 <= tEnd:
y0 += rungeStep(f, t0, y0[0], h, params)
^
During: typing of assignment at <ipython-input-56-38d2ea70b889> (6)
File "<ipython-input-56-38d2ea70b889>", line 6:
def inagrate(f, t0, y0, tEnd, h, params):
<source elided>
while t0 <= tEnd:
y0 += rungeStep(f, t0, y0[0], h, params)
^
没有 njit-decorator
它工作正常。有人可以帮我吗?
如果没有 JIT,应该会出现同样的错误,但也许广播规则足够灵活。你的 y0
是一个二维数组,你传递给 rungeStep
(Heun-Kutta 方法)的是一个一维数组,它的 return 值也是。所以你不能立即使用那个结果来更新 y0
,你必须更新 y0[0]
。但是接下来的问题是为什么首先要引入这种复杂性。
Numpy 数组被优化为固定大小的对象,追加到它们需要在每个实例中分配内存和复制,这比追加到列表慢,在列表中重新分配是通过(增长的)预留完成的。 Numba-JIT 在将 numpy 数组列表转换为 2d numpy 数组时似乎有问题。有效的是手动将数组降级为简单列表。经过一些反复试验,我得到了以下代码 运行(仅重复有更改的部分):
@njit
def rungeStep(f, t, y0, tau, params):
k1 = tau * f(t, y0, *params)
k2 = tau * f(t + tau/2, y0 + k1 / 2, *params)
k3 = tau * f(t + tau/2, y0 + k2 / 2, *params)
k4 = tau * f(t + tau, y0 + k3, *params)
return (k1 + 2 * k2 + 2 * k3 + k4) / 6
@njit
def integrate(f, t0, y0, tEnd, h, params):
ys = [list(y0)]
t = [t0]
while t0 <= tEnd:
y0 += rungeStep(f, t0, y0, h, params)
t0 += h
ys.append(list(y0))
t.append(t0)
return np.array(t), np.array(ys).T
args = (0.25, 5.0)
y0 = np.array([np.pi - 0.1, 0.0])
t, y = integrate(pend, 0, y0, 10, 1e-1, args)
请注意,您的 RK4 实现有一些遗漏,这里并不重要,但时间相关的 ODE 示例的错误来源。
绘制结果给出合理的图形
我正在尝试使用 Numba 加快我的 ODE 求解器的速度,但以下代码会引发键入错误:
import numpy as np
import matplotlib.pyplot as plt
from numba import njit
@njit
def pend(t, y, b, c):
theta, omega = y
dydt = np.array([omega, -b*omega - c*np.sin(theta)])
return dydt
@njit
def rungeStep(f, t, y0, tau, params):
k1 = tau * f(t, y0, *params)
k2 = tau * f(t, y0 + k1 / 2, *params)
k3 = tau * f(t, y0 + k2 / 2, *params)
k4 = tau * f(t, y0 + k3, *params)
return (k1 + 2 * k2 + 2 * k3 + k4) / 6
@njit
def integrate(f, t0, y0, tEnd, h, params):
ys = y0.copy()
t = np.array(t0)
while t0 <= tEnd:
y0 += rungeStep(f, t0, y0[0], h, params)
t0 += h
ys = np.concatenate((ys, y0), axis=0)
t = np.append(t, t0)
return t, ys.T
args = (0.25, 5)
y0 = np.array([[np.pi - 0.1, 0.0]])
t, y = integrate(pend, 0, y0, 10, 1, args)
这导致:
TypingError: Failed in nopython mode pipeline (step: nopython frontend)
Cannot unify array(int64, 0d, C) and array(int64, 1d, C) for 't.2', defined at <ipython-input-56-38d2ea70b889> (6)
File "<ipython-input-56-38d2ea70b889>", line 6:
def inagrate(f, t0, y0, tEnd, h, params):
<source elided>
while t0 <= tEnd:
y0 += rungeStep(f, t0, y0[0], h, params)
^
During: typing of assignment at <ipython-input-56-38d2ea70b889> (6)
File "<ipython-input-56-38d2ea70b889>", line 6:
def inagrate(f, t0, y0, tEnd, h, params):
<source elided>
while t0 <= tEnd:
y0 += rungeStep(f, t0, y0[0], h, params)
^
没有 njit-decorator
它工作正常。有人可以帮我吗?
如果没有 JIT,应该会出现同样的错误,但也许广播规则足够灵活。你的 y0
是一个二维数组,你传递给 rungeStep
(Heun-Kutta 方法)的是一个一维数组,它的 return 值也是。所以你不能立即使用那个结果来更新 y0
,你必须更新 y0[0]
。但是接下来的问题是为什么首先要引入这种复杂性。
Numpy 数组被优化为固定大小的对象,追加到它们需要在每个实例中分配内存和复制,这比追加到列表慢,在列表中重新分配是通过(增长的)预留完成的。 Numba-JIT 在将 numpy 数组列表转换为 2d numpy 数组时似乎有问题。有效的是手动将数组降级为简单列表。经过一些反复试验,我得到了以下代码 运行(仅重复有更改的部分):
@njit
def rungeStep(f, t, y0, tau, params):
k1 = tau * f(t, y0, *params)
k2 = tau * f(t + tau/2, y0 + k1 / 2, *params)
k3 = tau * f(t + tau/2, y0 + k2 / 2, *params)
k4 = tau * f(t + tau, y0 + k3, *params)
return (k1 + 2 * k2 + 2 * k3 + k4) / 6
@njit
def integrate(f, t0, y0, tEnd, h, params):
ys = [list(y0)]
t = [t0]
while t0 <= tEnd:
y0 += rungeStep(f, t0, y0, h, params)
t0 += h
ys.append(list(y0))
t.append(t0)
return np.array(t), np.array(ys).T
args = (0.25, 5.0)
y0 = np.array([np.pi - 0.1, 0.0])
t, y = integrate(pend, 0, y0, 10, 1e-1, args)
请注意,您的 RK4 实现有一些遗漏,这里并不重要,但时间相关的 ODE 示例的错误来源。
绘制结果给出合理的图形