使用类 archipelago#

../_images/archi_no_text.png

archipelago 类是 pygmo 的主要并行化引擎。它本质上是一个包含 island 的容器,能够异步启动每个 island 中的进化(优化任务),同时跟踪结果和任务之间的信息交换(通过广义岛屿模型进行迁移)。archipelago 中的各个 island 可以是异构的,并且可以引用不同的 UDA、UDP 和 UDI。

在本教程中,我们将展示如何使用archipelago来并行运行在UDP中定义的多个优化任务。因此,让我们开始定义我们的UDP。我们将在这里定义一个具有一个等式约束和一个不等式约束的单目标问题。问题本身对于本教程的目的并不重要。其数学定义为:

\[\begin{split}\begin{array}{ll} \mbox{minimize: } & \sum_i x_i \\ \mbox{subject to:} & -1 \le x_i \le 1, i = 1..n \\ & \sum_i x_i^2 = 1 \\ & \sum_i x_i \ge 0 \\ \end{array}\end{split}\]

目标函数的最优值为0。我们可以将上述问题写成一个pygmo UDP(参见: 编写一个简单的用户定义问题编写一个带约束的用户定义问题(NLP) 以了解如何从Python定义UDP)

>>> class toy_problem:
...     def __init__(self, dim):
...         self.dim = dim
...
...     def fitness(self, x):
...         return [sum(x), 1 - sum(x*x), - sum(x)]
...
...     def gradient(self, x):
...         return pg.estimate_gradient(lambda x: self.fitness(x), x) # numerical gradient
...
...     def get_nec(self):
...         return 1
...
...     def get_nic(self):
...         return 1
...
...     def get_bounds(self):
...         return ([-1] * self.dim, [1] * self.dim)
...
...     def get_name(self):
...         return "A toy problem"
...
...     def get_extra_info(self):
...         return "\tDimensions: " + str(self.dim)

现在,不再多说,让我们充分利用pygmo的全部功能,并准备好被震撼:

>>> import pygmo as pg
>>> a_cstrs_sa = pg.algorithm(pg.cstrs_self_adaptive(iters=1000))
>>> p_toy = pg.problem(toy_problem(50))
>>> p_toy.c_tol = [1e-4, 1e-4]
>>> archi = pg.archipelago(n=32,algo=a_cstrs_sa, prob=p_toy, pop_size=70)
>>> print(archi) 
Number of islands: 32
Status: idle

Islands summaries:

    #  Type                    Algo                                Prob           Size  Status
    --------------------------------------------------------------------------------------------
    0  Multiprocessing island  Self-adaptive constraints handling  A toy problem  70    idle
    1  Multiprocessing island  Self-adaptive constraints handling  A toy problem  70    idle
    2  Multiprocessing island  Self-adaptive constraints handling  A toy problem  70    idle
    3  Multiprocessing island  Self-adaptive constraints handling  A toy problem  70    idle
    4  Multiprocessing island  Self-adaptive constraints handling  A toy problem  70    idle
    5  Multiprocessing island  Self-adaptive constraints handling  A toy problem  70    idle
    6  Multiprocessing island  Self-adaptive constraints handling  A toy problem  70    idle
    7  Multiprocessing island  Self-adaptive constraints handling  A toy problem  70    idle
    ...

要实例化archipelago,我们使用了来自problemalgorithm的构造函数。 这留给archi对象构建各种种群和岛屿的任务。为此,为用户做出了几个选择,从岛屿类型开始。在这种情况下,从archi对象的屏幕打印输出中可以看出,正在使用多处理岛屿。为了控制哪些岛屿将组成一个群岛,用户可以实例化一个空的群岛,并使用pygmo.archipelago.push_back()方法逐个插入所有岛屿。

注意

在这种情况下,由archipelago构造函数选择的岛屿类型是multiprocessing islandpygmo.mp_island),因为我们在py36和Linux机器上运行此示例。通常,所选择的岛屿取决于平台、种群和算法,并且这种选择在island类构造函数的文档中有描述。

检查后,现在让我们运行进化。

>>> archi.get_champions_f() 
[array([  2.18826798, -26.60899368,  -2.18826798]),
array([  3.39497588, -24.48739141,  -3.39497588]),
array([  2.3240917 , -26.88225527,  -2.3240917 ]),
array([  0.13134093, -28.47299705,  -0.13134093]),
array([  6.53062434, -24.98724057,  -6.53062434]),
array([  1.02894159, -25.69765425,  -1.02894159]),
array([  4.07802374, -23.82020921,  -4.07802374]),
array([  1.71396489, -25.90794514,  -1.71396489]),
...]
>>> archi.evolve() 
>>> print(archi) 
Number of islands: 32
Status: busy

Islands summaries:

    #  Type                    Algo                                Prob           Size  Status
    --------------------------------------------------------------------------------------------
    0  Multiprocessing island  Self-adaptive constraints handling  A toy problem  70    busy
    1  Multiprocessing island  Self-adaptive constraints handling  A toy problem  70    busy
    2  Multiprocessing island  Self-adaptive constraints handling  A toy problem  70    busy
    3  Multiprocessing island  Self-adaptive constraints handling  A toy problem  70    busy
    4  Multiprocessing island  Self-adaptive constraints handling  A toy problem  70    busy
    5  Multiprocessing island  Self-adaptive constraints handling  A toy problem  70    busy
    6  Multiprocessing island  Self-adaptive constraints handling  A toy problem  70    busy
    7  Multiprocessing island  Self-adaptive constraints handling  A toy problem  70    busy
    ...

注意进化是如何在32个独立线程上并行发生的(每个线程生成一个进程,在这种情况下,使用多处理岛)。 进化是异步发生的,因此不会直接干扰我们的主进程。然后我们必须调用pygmo.archipelago.wait() 方法,使主进程显式等待所有岛完成。

>>> archi.wait()
>>> archi.get_champions_f() 
[array([  1.16514064e-02,   4.03450637e-05,  -1.16514064e-02]),
array([ 0.02249111,  0.00392739, -0.02249111]),
array([  6.09564060e-03,  -4.93961313e-05,  -6.09564060e-03]),
array([ 0.01161224, -0.00815189, -0.01161224]),
array([ -1.90431378e-05,   7.65501702e-05,   1.90431378e-05]),
array([ -5.45044897e-05,   5.70199057e-05,   5.45044897e-05]),
array([ 0.00541601, -0.08208163, -0.00541601]),
array([ -6.95677113e-05,  -7.42268924e-05,   6.95677113e-05]),
array([ 0.00335729,  0.00684969, -0.00335729]),
...

不同的岛屿会产生不同的结果,在这种情况下,由于各种种群和算法是使用随机种子构建的。

注意

这里选择使用UDA cstrs_self_adaptive只是为了保持各个线程的忙碌, 在这种情况下,其他本地算法很可能是更好的选择。

管理异常#

如果在发送到island的优化任务期间发生异常,会发生什么?这个问题已经在island教程中探讨过,由于archipelago基本上是多个island的容器,这里我们将与该教程的部分内容重叠,探讨在archipelago上下文中抛出的异常。

为了展示pygmo如何处理这些情况,我们使用下面的假问题,一旦进行了300次适应度评估就立即抛出。

>>> class raise_exception:
...     def __init__(self):
...         self.counter=0;
...     def fitness(self,dv):
...         if self.counter == 300:
...             raise
...         self.counter += 1
...         return [0]
...     def get_bounds(self):
...         return ([0],[1])
...     def get_name(self):
...         return "A throwing UDP"

现在让我们实例化并运行一个archipelago

>>> archi = pg.archipelago(n = 5, algo = pg.simulated_annealing(Ts = 10, Tf = 0.1, n_T_adj  = 40), prob = raise_exception(), pop_size = 20)
>>> archi.evolve() 
>>> archi.wait()
>>> print(archi) 
Number of islands: 5
Status: idle - **error occurred**

Islands summaries:

    #  Type                    Algo                            Prob            Size  Status
    ------------------------------------------------------------------------------------------------------------
    0  Multiprocessing island  Simulated Annealing (Corana's)  A throwing UDP  20    idle - **error occurred**
    1  Multiprocessing island  Simulated Annealing (Corana's)  A throwing UDP  20    idle - **error occurred**
    2  Multiprocessing island  Simulated Annealing (Corana's)  A throwing UDP  20    idle - **error occurred**
    3  Multiprocessing island  Simulated Annealing (Corana's)  A throwing UDP  20    idle - **error occurred**
    4  Multiprocessing island  Simulated Annealing (Corana's)  A throwing UDP  20    idle - **error occurred**

打印语句允许我们直观地看到所有岛屿中都发生了错误(每个岛屿都包含问题的副本,因此从0次适应度评估开始,然后达到300次评估,触发raise语句)。 在这个阶段,我们不知道具体发生了什么,因为异常是在单独的线程/进程中抛出的,因此它对我们的主范围是隐藏的。为了检查发生了什么,我们可以编写:

>>> archi.wait_check() 
RuntimeError                              Traceback (most recent call last)
<ipython-input-22-d8562cc51573> in <module>()
----> 1 archi.wait_check()

RuntimeError: The asynchronous evolution of a pythonic island of type 'Multiprocessing island' raised an error:
multiprocessing.pool.RemoteTraceback:
"""
Traceback (most recent call last):
File "/home/dario/miniconda3/envs/pagmo/lib/python3.6/multiprocessing/pool.py", line 119, in worker
    result = (True, func(*args, **kwds))
File "/home/dario/miniconda3/envs/pagmo/lib/python3.6/site-packages/pygmo/_py_islands.py", line 40, in _evolve_func
    return algo.evolve(pop)
File "<ipython-input-19-f5ede4c63180>", line 6, in fitness
RuntimeError: No active exception to reraise
"""

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
File "/home/dario/miniconda3/envs/pagmo/lib/python3.6/site-packages/pygmo/_py_islands.py", line 128, in run_evolve
    return res.get()
File "/home/dario/miniconda3/envs/pagmo/lib/python3.6/multiprocessing/pool.py", line 608, in get
    raise self._value
RuntimeError: No active exception to reraise

这将重新抛出第一个遇到的异常,并将所有岛屿状态重置为idle