2015-12-15 4 views
11

Я хотел бы использовать scipy.integrate.ode (или scipy.integrate.odeint) экземпляры в нескольких потоков (по одному для каждого ядра процессора) для того, чтобы решить несколько IVPs на время. Однако документация говорит: «интегратора одновременно„“. Этот интегратора не Реентрантный Вы не можете иметь два экземпляра оды с помощью» ВодыНесколько экземпляров scipy.integrate.ode

(Также odeint вызывает внутренние ошибки, если экземпляр несколько раз хотя в документации не сказано).

Любая идея, что можно сделать?

+2

Вы видели [этот комментарий] (http://stackoverflow.com/questions/23574146/solving-two-uncoupled-odes-within -a-loop-using-python-and-scipy-integrate-ode # comment36180664_23574361) и соответствующие сообщения? Кроме того, odeint [не может быть восприимчивым] (http://comments.gmane.org/gmane.comp.python.scientific.user/36151) к той же проблеме. –

+0

@ AndrasDeak: Спасибо, что посмотрели! Ссылка на .pdf больше не работала. Однако мне скорее нужен неявный решатель, чем явный, и Рунге Кутта явный, я думаю. У меня также были внутренние ошибки, когда я пытался использовать mutithreaded odeint, а не ode. Я думаю, что 'ode.set_integrator ('lsoda')' является той же реализацией, что и odeint. – Matthias

ответ

8

Один из вариантов заключается в использовании multiprocessing (т. Е. Использовать процессы вместо потоков). Вот пример, который использует функцию map класса multiprocessing.Pool.

Функция solve принимает набор начальных условий и возвращает решение, генерируемое odeint. «Серийная» версия кода в основном разделе вызывает solve несколько раз, один раз для каждого набора начальных условий в ics. Версия «многопроцессорной обработки» использует функцию map экземпляра multiprocessing.Pool для одновременного запуска нескольких процессов, каждый из которых вызывает solve. Функция map позаботится о том, чтобы доставить аргументы до solve.

Мой компьютер имеет четыре ядра, и по мере увеличения num_processes ускорение достигает 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.") 

На моем компьютере, выход:

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

Большое спасибо за пример! Это то, чего я хотел. – Matthias

+0

Знаете ли вы, почему загрузка процессора всегда соответствует количеству процессов (вплоть до количества ядер), а не заданному размеру начальных условий? – Matthias

+0

Это звучит разумно. В конце концов, как загрузка процессора может превышать количество ядер? –