多个 scipy.integrate.ode 实例

Posted

技术标签:

【中文标题】多个 scipy.integrate.ode 实例【英文标题】:Multiple scipy.integrate.ode instances 【发布时间】:2016-03-21 09:16:07 【问题描述】:

我想在 多个线程(每个 CPU 内核一个)中使用 scipy.integrate.ode(或 scipy.integrate.odeint)实例来解决一次进行多个 IVP。但是文档说:“此集成器不可重入。您不能同时使用“vode”集成器的两个 ode 实例。

(尽管文档没有说明,但如果多次实例化,也 odeint 会导致内部错误。)

知道可以做什么吗?

【问题讨论】:

你看过this comment和相应的帖子吗?另外,odeint might no be susceptible 解决同样的问题。 @AndrasDeak:感谢您的关注! .pdf 的链接不再起作用。然而,我宁愿需要一个隐式求解器而不是一个显式求解器,我认为 Runge Kutta 是一个显式求解器。当我尝试使用多线程 odeint 而不是 ode 时,我也遇到了内部错误。我认为ode.set_integrator('lsoda') 与 odeint 的实现相同。 【参考方案1】:

一种选择是使用multiprocessing(即使用进程而不是线程)。这是一个使用multiprocessing.Pool 类的map 函数的示例。

函数solve 采用一组初始条件并返回由odeint 生成的解。主要部分中代码的“串行”版本重复调用solve,对ics 中的每组初始条件调用一次。 “多处理”版本使用multiprocessing.Pool 实例的map 函数同时运行多个进程,每个进程调用solvemap 函数负责将参数分配给 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

【讨论】:

非常感谢这个例子!这就是我想要的。 你知道为什么CPU负载总是与进程数(最多核数)相对应,而不是与初始条件的设定大小相对应吗? 这听起来很合理。毕竟CPU负载怎么会超过核心数呢? 例如如果初始条件的设置大小为 2(即有两个单独的 ode 计算)并且进程数为 4(在 4 核机器上),尽管每个 ode 只能使用 1 个内核,但 CPU 负载将始终为 100%计算。我希望 CPU 负载保持在 50% 左右,并且有 2 个进程保持空闲。 solve 的每次调用都在单个进程中运行。使用两组初始条件,您可以期望的最好的结果是 100% 使用两个 CPU。这就是我看到 make ics 是否具有形状 (2, 3),并且我设置了 num_processes=4,但我必须使 t 数组变大才能使用 Mac OS X 上的活动监视器清楚地看到它。在这种情况下,多处理只有在每个解决方案都需要很长时间时才有用,否则多处理开销时间(例如启动进程的时间、在进程之间复制数据的时间等)将占主导地位。【参考方案2】:

SciPy.integrate.ode 似乎使用了 LLNL SUNDIALS solvers,虽然 SciPy 没有使用 say so explicitly,但在我看来,它们应该使用。

CVODE ode 求解器的当前版本 3.2.2 是可重入的,这意味着它可以用于同时解决多个问题。相关信息出现在User Documentation for CVODE v3.2.0 (SUNDIALS v3.2.0)。

cvode 用于解决给定问题的所有状态信息都保存在一个结构中,以及一个指针 将该结构返回给用户。 cvode 包中没有全局数据,所以,在这个 尊重,它是可重入的。特定于线性求解器的状态信息保存在单独的结构中, 一个指针,它驻留在 cvode 内存结构中。 cvode的重入被激发了 通过预期的多计算机扩展,但在单处理器设置中也是必不可少的,其中两个 一个或多个问题可以通过在单个用户程序中对包的混合调用来解决。

但我不知道 SciPy.integrate.ode 或其他像 scikits.odes.ode 这样的 ode 求解器是否支持这种并发。

【讨论】:

以上是关于多个 scipy.integrate.ode 实例的主要内容,如果未能解决你的问题,请参考以下文章

scipy.integrate.ode 放弃集成

scipy.integrate.ode 的内部工作

scipy.integrate.ode 与两个耦合的 ODE?

通过 scipy.integrate.ode 使用自适应步长

如何找到 scipy.integrate.ode 的默认 atol 和 rtol?

将 numba.jit 与 scipy.integrate.ode 一起使用