Opzioni per risolvere i sistemi ODE su GPU?


15

Vorrei coltivare i sistemi di risoluzione degli ODE su GPU, in un contesto "banalmente parallelizzabile". Ad esempio, facendo un'analisi di sensibilità con 512 diversi set di parametri.

Idealmente, voglio fare la risoluzione ODE con un solutore di timestep adattivo intelligente come CVODE, piuttosto che un timestep fisso come Forward Euler, ma eseguendolo su una GPU NVIDIA anziché su CPU.

Qualcuno l'ha fatto? Ci sono librerie per questo?


Da qui le parentesi! Sto prendendo in considerazione una tecnica basata sulla suddivisione dell'operatore (simulazioni di elettrofisiologia cardiaca), in cui si risolvono gli ODE sui nodi per ottenere un termine sorgente per PDE, quindi si modifica un parametro ODE per la successiva iterazione.
mirams,


1
È importante specificare se si desidera utilizzare lo stesso time-stepping per ogni ODE o meno.
Christian Clason,

Inoltre, se sei particolarmente interessato alle equazioni bidomain (o monodomain), potresti voler dare un'occhiata a come CARP lo fa .
Christian Clason,

Timestep diversi, se il metodo è adattivo, ne avrà bisogno per diversi set di parametri ... grazie per il link a ciò che fa CARP - un risolutore ODE Rush Larsen timestep fisso se l'ho letto correttamente.
mirams,

Risposte:


6

Potresti voler esaminare la libreria di Odeint di Boost e Thrust . Possono essere combinati come discusso qui .


Questo sembra essere un po 'diverso: risolvere in parallelo i sistemi ODE di massa sulla GPU (con comunicazione). Questo link dice "Abbiamo sperimentato che la dimensione del vettore su cui è parallelizzato dovrebbe essere dell'ordine di 10 ^ 6 per sfruttare appieno la GPU.". Sto cercando un bel modo di coltivare O O (10) o O (100) vettore ODE banalmente parallelamente
risolvibile

Hai pensato di scrivere direttamente in cuda o openCL? Se ho capito bene, quello che stai facendo è iterare su alcune equazioni ODE in ogni thread separatamente, non dovrebbe essere difficile scriverlo direttamente.
Hydro Guy,

Immagino che sarebbe possibile codificare un Forward Euler o un altro metodo di timestep fisso, in cui ogni processo GPU utilizza lo stesso timestep, abbastanza facilmente, mi piacerebbe sapere se qualcuno è riuscito a ottenere timestrap adattivo come CVODE funzionante, o se questo è impossibile renderlo efficiente su una GPGPU?
mirams,

il problema con gpu è che devi scrivere codice parallelo ai dati. Se scrivi la stessa routine adattiva ma assorbendo tutta quella flessibilità sui valori di alcuni parametri, probabilmente è possibile codificarlo in modo efficiente su gpu. Ciò significa anche che non è possibile utilizzare la ramificazione sulle istruzioni, che è probabilmente ciò che pensi che renderebbe impossibile farlo.
Hydro Guy,

1
@mirams c'è un esempio di odeint che copre esattamente ciò che stai cercando: boost.org/doc/libs/1_59_0/libs/numeric/odeint/doc/html/… , vedi anche github.com/boostorg/odeint/blob/ maestro / esempi / spinta /… . Inoltre, odeint supporta metodi multistep adattivi come in CVODE: github.com/boostorg/odeint/blob/master/examples/…
Christian Clason,

12

La libreria DifferentialEquations.jl è una libreria per un linguaggio di alto livello (Julia) che ha strumenti per trasformare automaticamente il sistema ODE in una versione ottimizzata per soluzioni parallele su GPU. Esistono due forme di parallelismo che possono essere impiegate: parallelismo basato su array per sistemi ODE di grandi dimensioni e parallelismo dei parametri per studi di parametri su sistemi ODE relativamente piccoli (<100). Supporta metodi impliciti ed espliciti di ordine elevato e sovraperforma regolarmente o abbina altri sistemi nei benchmark (almeno, avvolge gli altri, quindi è facile controllarli e usarli!)

Per questa specifica funzionalità, potresti voler dare un'occhiata a DiffEqGPU.jl che è il modulo per il parallelismo automatico dei parametri. La libreria DifferentialEquations.jl ha funzionalità per studi di parametri paralleli e questo modulo migliora le configurazioni esistenti per far sì che lo studio avvenga automaticamente in parallelo. Quello che uno fa è trasformare il loro esistente ODEProblem(o altro DEProblemsimile SDEProblem) in un EnsembleProbleme specificare con un prob_funccome gli altri problemi sono generati dal prototipo. Quanto segue risolve 10.000 traiettorie dell'equazione di Lorenz sulla GPU con un metodo adattivo esplicito di alto ordine:

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)

Si noti che l'utente non ha bisogno di scrivere alcun codice GPU e con un singolo RTX 2080 questo benchmark è un miglioramento di 5 volte rispetto all'utilizzo di una macchina Xeon a 16 core con parallelismo multithread. Si può quindi controllare il README per come fare cose come utilizzare più GPU e fare multiprocessing + GPU per utilizzare contemporaneamente un intero cluster di GPU . Si noti che il passaggio al multithreading anziché alle GPU è un cambio di riga: EnsembleThreads()anziché EnsembleGPUArray().

Quindi, per i risolutori impliciti, vale la stessa interfaccia. Ad esempio, quanto segue utilizza Rosenbrock di alto ordine e i metodi impliciti di 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)

Mentre questo modulo richiede che tu dia un giacobino per essere usato sulla GPU (attualmente, verrà risolto presto), la documentazione DifferentialEquations.jl mostra come eseguire calcoli simbolici giacobini simbolici su funzioni definite numericamente , quindi non c'è ancora nessun manuale lavoro qui. Consiglio vivamente questi algoritmi perché la logica di ramificazione di un metodo come CVODE generalmente provoca la desincronizzazione dei thread e non sembra funzionare come un metodo Rosenbrock in questi tipi di scenari.

Usando DifferentialEquations.jl, puoi anche accedere alla libreria completa, che include funzionalità come l'analisi della sensibilità globale che può fare uso di questa accelerazione GPU. È inoltre compatibile con due numeri per un'analisi rapida della sensibilità locale . Il codice basato su GPU ottiene tutte le funzionalità di DifferentialEquations.jl, come la gestione degli eventi e l' ampia serie di solutori ODE che sono ottimizzati per diversi tipi di problemi , il che significa che non è solo un semplice solutore ODE GPU una tantum, ma invece un parte di un sistema completo che ha anche un efficiente supporto GPU.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.