Varias instancias de scipy.integrate.ode

Me gustaría usar instancias de scipy.integrate.ode (o scipy.integrate.odeint) en varios subprocesos (uno para cada núcleo de la CPU) para resolver múltiples IVP a la vez. Sin embargo, la documentación dice: ” Este integrador no es reingresante. No puede tener dos instancias de oda utilizando el integrador” vode “al mismo tiempo.

(También odeint causa errores internos si se crea una instancia varias veces, aunque la documentación no lo dice).

¿Alguna idea de qué se puede hacer?

Una opción es usar multiprocessing (es decir, usar procesos en lugar de subprocesos). Aquí hay un ejemplo que usa la función de map de la clase multiprocessing.Pool .

La función solve toma un conjunto de condiciones iniciales y devuelve una solución generada por odeint . La versión “serial” del código en la sección principal de llamadas se solve repetidamente, una vez para cada conjunto de condiciones iniciales en ics . La versión de “multiprocesamiento” usa la función de map de una instancia de multiprocessing.Pool para ejecutar varios procesos simultáneamente, y cada una de ellas solve . La función de map se encarga de repartir los argumentos para solve .

Mi computadora tiene cuatro núcleos, y a medida que incremento el número de num_processes , la aceleración máxima es de aproximadamente 3.6.

 from __future__ import division, print_function import sys import time import multiprocessing as mp import numpy as np from scipy.integrate import odeint def lorenz(q, t, sigma, rho, beta): x, y, z = q return [sigma*(y - x), x*(rho - z) - y, x*y - beta*z] def solve(ic): t = np.linspace(0, 200, 801) sigma = 10.0 rho = 28.0 beta = 8/3 sol = odeint(lorenz, ic, t, args=(sigma, rho, beta), rtol=1e-10, atol=1e-12) return sol if __name__ == "__main__": ics = np.random.randn(100, 3) print("multiprocessing:", end='') tstart = time.time() num_processes = 5 p = mp.Pool(num_processes) mp_solutions = p.map(solve, ics) tend = time.time() tmp = tend - tstart print(" %8.3f seconds" % tmp) print("serial: ", end='') sys.stdout.flush() tstart = time.time() serial_solutions = [solve(ic) for ic in ics] tend = time.time() tserial = tend - tstart print(" %8.3f seconds" % tserial) print("num_processes = %i, speedup = %.2f" % (num_processes, tserial/tmp)) check = [(sol1 == sol2).all() for sol1, sol2 in zip(serial_solutions, mp_solutions)] if not all(check): print("There was at least one discrepancy in the solutions.") 

En mi computadora, la salida es:

 multiprocessing: 6.904 seconds serial: 24.756 seconds num_processes = 5, speedup = 3.59 

SciPy.integrate.ode parece usar los solucionadores LLNL SUNDIALS , aunque SciPy no lo dice explícitamente , pero deberían, en mi opinión.

La versión actual de CVODE ode solver, 3.2.2, es reingresante, lo que significa que puede usarse para resolver múltiples problemas al mismo tiempo. La información relevante aparece en la documentación del usuario para CVODE v3.2.0 (SUNDIALS v3.2.0) .

Toda la información de estado utilizada por cvode para resolver un problema dado se guarda en una estructura, y un puntero a esa estructura se devuelve al usuario. No hay datos globales en el paquete cvode, por lo que, a este respecto, es reentrante. La información de estado específica del solucionador lineal se guarda en una estructura separada, un puntero al que reside en la estructura de memoria de cvode. La reentrada de cvode fue motivada por la extensión anticipada de múltiples computadoras, pero también es esencial en una configuración de un solo procesador donde dos o más problemas se resuelven mediante llamadas entremezcladas al paquete desde un solo progtwig de usuario.

Pero no sé si SciPy.integrate.ode, u otros solucionadores de odas como scikits.odes.ode , admiten esta concurrencia.