在GPU上解决ODE系统的选项?


15

我想在“平凡的可并行化”设置下将ODE的求解系统投放到GPU上。例如,使用512个不同的参数集进行灵敏度分析。

理想情况下,我想使用智能自适应时间步长求解器(例如CVODE)而不是固定时间步长(例如Forward Euler)来执行ODE解决方案,而是在NVIDIA GPU而不是CPU上运行它。

有人这样做吗?有图书馆吗?


因此括号!我正在考虑一种基于操作员拆分的技术(心脏电生理模拟),您可以在节点上求解ODE以获得PDE的源项,然后更改ODE参数以进行下一次迭代。
mirams 2014年


1
重要的是要指定是否要对每个ODE使用相同的时间步长。
克里斯蒂安·克拉森2015年

另外,如果您对双域(或单域)方程式特别感兴趣,则可能需要看一下CARP的工作方式
克里斯蒂安·克拉森2015年

不同的时间步长,如果该方法是自适应的,那么它将需要它们用于不同的参数集...感谢与CARP所做的链接-如果我正确阅读,则为固定的时间步长Rush Larsen ODE求解器。
mirams

Answers:


6

您可能需要研究Boost的odeint库Thrust。它们可以如此处讨论的那样组合。


这似乎有点不同-并行(通过通信)在GPU上解决大型ODE系统。该链接说:“我们已经体验到,要充分利用GPU,并行化的向量大小应为10 ^ 6左右。” 我正在寻找一种解决O(10)或O(100)向量大小的平凡可并行ODE求解的好方法...
mirams 2014年

您是否考虑过直接使用cuda或openCL编写代码?如果我理解正确,那么您正在做的是分别遍历每个线程中的某些ODE方程,那么直接编写它就不难了。
Hydro Guy

我想可以编写一个正向Euler或其他固定的时间步长方法,其中每个GPU进程都使用相同的时间步长,相当容易,我想知道是否有人像CVODE那样设法获得了自适应的时间步长,或者是否在GPGPU上提高效率是不可能的吗?
mirams

gpu的问题是您需要编写数据并行代码。如果编写相同的自适应例程,但在某些参数的值上吸收了所有的灵活性,则可能可以在gpu上高效地对其进行编码。这也意味着您不能在指令上使用分支,这可能是您认为使其无法执行的事情。
Hydro Guy

1
@mirams有一个odeint的示例,它完全涵盖了您要查找的内容:boost.org/doc/libs/1_59_0/libs/numeric/odeint/doc/html/…,另请参见github.com/boostorg/odeint/blob/主人/例子/推力/…。此外,odeint支持CVODE中的自适应多步方法:github.com/boostorg/odeint/blob/master/examples/…–
Christian

12

DifferentialEquations.jl库是用于高级语言(Julia)的库,该库具有用于将ODE系统自动转换为针对GPU并行解决方案的优化版本的工具。可以采用两种形式的并行性:用于大型ODE系统的基于数组的并行性和用于相对较小(<100)ODE系统的参数研究的参数并行性。它支持高阶隐式和显式方法,并且在性能上通常优于或匹配基准测试中的其他系统(至少,它包装了其他系统,因此很容易检查和使用它们!)

对于此特定功能,您可能需要查看DiffEqGPU.jl,它是用于自动参数并行化的模块。DifferentialEquations.jl库具有用于并行参数研究的功能,并且该模块扩充了现有配置,以使研究自动并行进行。一个人所做的就是将其现有的ODEProblem(或其他DEProblem类似的SDEProblem)转换为,EnsembleProblem并指定prob_func如何从原型中产生其他问题。以下内容采用高阶显式自适应方法求解了GPU上10,000条Lorenz方程的轨迹:

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文档演示了如何对数字定义的函数执行自动符号雅可比计算,因此仍然没有手册在这里工作。我强烈建议使用这些算法,因为像CVODE这样的方法的分支逻辑通常会导致线程不同步,并且在这些类型的场景中反而看起来不像Rosenbrock方法那样好。

通过使用DifferentialEquations.jl,您还可以访问完整的库,其中包括诸如全局灵敏度分析之类的功能,可以利用此GPU加速功能。它还与双数兼容,可进行快速的局部灵敏度分析。基于GPU的代码获得了DifferentialEquations.jl的所有功能,例如事件处理针对不同类型问题进行了优化大量ODE求解器,这意味着它不仅是简单的一次性GPU ODE求解器,而且功能齐全的系统的一部分,该系统也恰好具有有效的GPU支持。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.