我想在“可并行化”的设置中将 ODE 的求解系统外包到 GPU 上。例如,使用 512 个不同的参数集进行敏感性分析。
理想情况下,我想使用像 CVODE 这样的智能自适应时间步长求解器进行 ODE 求解,而不是像 Forward Euler 这样的固定时间步长,而是在 NVIDIA GPU 而不是 CPU 上运行它。
有人做过吗?有图书馆吗?
我想在“可并行化”的设置中将 ODE 的求解系统外包到 GPU 上。例如,使用 512 个不同的参数集进行敏感性分析。
理想情况下,我想使用像 CVODE 这样的智能自适应时间步长求解器进行 ODE 求解,而不是像 Forward Euler 这样的固定时间步长,而是在 NVIDIA GPU 而不是 CPU 上运行它。
有人做过吗?有图书馆吗?
DifferentialEquations.jl库是用于高级语言 (Julia) 的库,它具有用于将 ODE 系统自动转换为优化版本以在 GPU 上并行解决方案的工具。可以采用两种形式的并行性:大型 ODE 系统的基于数组的并行性和相对较小 (<100) ODE 系统的参数研究的参数并行性。它支持高阶隐式和显式方法,并且在基准测试中通常优于或匹配其他系统(至少,它包装了其他系统,因此很容易检查和使用它们!)
对于此特定功能,您可能需要查看DiffEqGPU.jl,它是用于自动参数并行的模块。DifferentialEquations.jl 库具有并行参数研究的功能,并且该模块增强了现有配置以使研究自动并行进行。一个人所做的是将他们现有的ODEProblem
(或其他DEProblem
类似SDEProblem
的)转换为一个EnsembleProblem
,并用一个指定prob_func
其他问题是如何从原型中生成的。下面用高阶显式自适应方法在 GPU 上求解 Lorenz 方程的 10000 条轨迹:
using OrdinaryDiffEq, DiffEqGPU
function lorenz(du,u,p,t)
@inbounds begin
du[1] = p[1]*(u[2]-u[1])
du[2] = u[1]*(p[2]-u[3]) - u[2]
du[3] = u[1]*u[2] - p[3]*u[3]
end
nothing
end
u0 = Float32[1.0;0.0;0.0]
tspan = (0.0f0,100.0f0)
p = (10.0f0,28.0f0,8/3f0)
prob = ODEProblem(lorenz,u0,tspan,p)
prob_func = (prob,i,repeat) -> remake(prob,p=rand(Float32,3).*p)
monteprob = EnsembleProblem(prob, prob_func = prob_func)
@time sol = solve(monteprob,Tsit5(),EnsembleGPUArray(),trajectories=10_000,saveat=1.0f0)
请注意,用户无需编写 GPU 代码,并且使用单个 RTX 2080 与使用具有多线程并行性的 16 核 Xeon 机器相比,该基准性能提高了 5 倍。然后可以查看自述文件,了解如何执行诸如利用多个 GPU 和执行多处理 + GPU 以同时利用完整的 GPU 集群之类的事情。请注意,切换到多线程而不是 GPU 是一项更改:EnsembleThreads()
而不是EnsembleGPUArray()
.
然后对于隐式求解器,同样的接口成立。例如,以下使用高阶 Rosenbrock 和隐式 Runge-Kutta 方法:
function lorenz_jac(J,u,p,t)
@inbounds begin
σ = p[1]
ρ = p[2]
β = p[3]
x = u[1]
y = u[2]
z = u[3]
J[1,1] = -σ
J[2,1] = ρ - z
J[3,1] = y
J[1,2] = σ
J[2,2] = -1
J[3,2] = x
J[1,3] = 0
J[2,3] = -x
J[3,3] = -β
end
nothing
end
function lorenz_tgrad(J,u,p,t)
nothing
end
func = ODEFunction(lorenz,jac=lorenz_jac,tgrad=lorenz_tgrad)
prob_jac = ODEProblem(func,u0,tspan,p)
monteprob_jac = EnsembleProblem(prob_jac, prob_func = prob_func)
@time solve(monteprob_jac,Rodas5(linsolve=LinSolveGPUSplitFactorize()),EnsembleGPUArray(),dt=0.1,trajectories=10_000,saveat=1.0f0)
@time solve(monteprob_jac,TRBDF2(linsolve=LinSolveGPUSplitFactorize()),EnsembleGPUArray(),dt=0.1,trajectories=10_000,saveat=1.0f0)
虽然这种形式要求您提供 Jacobian 以便在 GPU 上使用(目前,将很快修复),但 DifferentialEquations.jl 文档演示了如何对数值定义的函数进行自动符号 Jacobian 计算,因此仍然没有手册在这里劳作。我强烈推荐这些算法,因为像 CVODE 这样的方法的分支逻辑通常会导致线程不同步,并且无论如何在这些类型的场景中执行起来不如 Rosenbrock 方法。
通过使用DifferentialEquations.jl,您还可以访问完整的库,其中包括可以利用这种GPU 加速的全局敏感性分析等功能。它还与双数兼容,可进行快速的局部灵敏度分析。基于 GPU 的代码获得了 DifferentialEquations.jl 的所有特性,例如事件处理和针对不同类型问题进行优化的大量 ODE 求解器,这意味着它不仅仅是一个简单的一次性 GPU ODE 求解器,而是一个功能齐全的系统的一部分,该系统也恰好具有高效的 GPU 支持。
您可能想查看Boost 的 odeint 库和Thrust。它们可以像这里讨论的那样组合。