1
|
|
|
# encoding=utf8 |
2
|
|
|
# pylint: disable=mixed-indentation, trailing-whitespace, multiple-statements, attribute-defined-outside-init, logging-not-lazy, redefined-builtin, line-too-long, no-self-use, arguments-differ, no-else-return, bad-continuation |
3
|
|
|
import logging |
4
|
|
|
from numpy import argsort, sum, apply_along_axis, where, pi, ceil, isinf |
5
|
|
|
from numpy.random import exponential |
6
|
|
|
import numpy as np |
7
|
|
|
from NiaPy.algorithms.algorithm import Algorithm |
8
|
|
|
|
9
|
|
|
__all__ = ['MonarchButterflyOptimization'] |
10
|
|
|
|
11
|
|
|
logging.basicConfig() |
12
|
|
|
logger = logging.getLogger('NiaPy.algorithms.basic') |
13
|
|
|
logger.setLevel('INFO') |
14
|
|
|
|
15
|
|
|
|
16
|
|
|
class MonarchButterflyOptimization(Algorithm): |
17
|
|
|
r"""Implementation of Monarch Butterfly Optimization. |
18
|
|
|
|
19
|
|
|
Algorithm: |
20
|
|
|
Monarch Butterfly Optimization |
21
|
|
|
|
22
|
|
|
Date: |
23
|
|
|
2019 |
24
|
|
|
|
25
|
|
|
Authors: |
26
|
|
|
Jan Banko |
27
|
|
|
|
28
|
|
|
License: |
29
|
|
|
MIT |
30
|
|
|
|
31
|
|
|
Reference paper: |
32
|
|
|
Wang, Gai-Ge & Deb, Suash & Cui, Zhihua. (2015). Monarch Butterfly Optimization. Neural Computing and Applications. 10.1007/s00521-015-1923-y. , https://www.researchgate.net/publication/275964443_Monarch_Butterfly_Optimization. |
33
|
|
|
|
34
|
|
|
Attributes: |
35
|
|
|
Name (List[str]): List of strings representing algorithm name. |
36
|
|
|
PAR (float): Partition. |
37
|
|
|
PER (float): Period. |
38
|
|
|
|
39
|
|
|
See Also: |
40
|
|
|
* :class:`NiaPy.algorithms.Algorithm` |
41
|
|
|
""" |
42
|
|
|
Name = ['MonarchButterflyOptimization', 'MBO'] |
43
|
|
|
|
44
|
|
|
@staticmethod |
45
|
|
|
def algorithmInfo(): |
46
|
|
|
return r""" |
47
|
|
|
Description: Monarch butterfly optimization algorithm is inspired by the migration behaviour of the monarch butterflies in nature. |
48
|
|
|
Authors: Wang, Gai-Ge & Deb, Suash & Cui, Zhihua. |
49
|
|
|
Year: 2015 |
50
|
|
|
Main reference: Wang, Gai-Ge & Deb, Suash & Cui, Zhihua. (2015). Monarch Butterfly Optimization. Neural Computing and Applications. 10.1007/s00521-015-1923-y. , https://www.researchgate.net/publication/275964443_Monarch_Butterfly_Optimization. |
51
|
|
|
""" |
52
|
|
|
|
53
|
|
|
@staticmethod |
54
|
|
|
def typeParameters(): |
55
|
|
|
r"""Get dictionary with functions for checking values of parameters. |
56
|
|
|
|
57
|
|
|
Returns: |
58
|
|
|
Dict[str, Callable]: |
59
|
|
|
* PAR (Callable[[float], bool]): Checks if partition parameter has a proper value. |
60
|
|
|
* PER (Callable[[float], bool]): Checks if period parameter has a proper value. |
61
|
|
|
See Also: |
62
|
|
|
* :func:`NiaPy.algorithms.algorithm.Algorithm.typeParameters` |
63
|
|
|
""" |
64
|
|
|
d = Algorithm.typeParameters() |
65
|
|
|
d.update({ |
66
|
|
|
'PAR': lambda x: isinstance(x, float) and x > 0, |
67
|
|
|
'PER': lambda x: isinstance(x, float) and x > 0 |
68
|
|
|
}) |
69
|
|
|
return d |
70
|
|
|
|
71
|
|
|
def setParameters(self, NP=20, PAR=5.0 / 12.0, PER=1.2, **ukwargs): |
72
|
|
|
r"""Set the parameters of the algorithm. |
73
|
|
|
|
74
|
|
|
Args: |
75
|
|
|
NP (Optional[int]): Population size. |
76
|
|
|
PAR (Optional[int]): Partition. |
77
|
|
|
PER (Optional[int]): Period. |
78
|
|
|
ukwargs (Dict[str, Any]): Additional arguments. |
79
|
|
|
|
80
|
|
|
See Also: |
81
|
|
|
* :func:`NiaPy.algorithms.Algorithm.setParameters` |
82
|
|
|
""" |
83
|
|
|
Algorithm.setParameters(self, NP=NP) |
84
|
|
|
self.NP, self.PAR, self.PER, self.keep, self.BAR, self.NP1 = NP, PAR, PER, 2, PAR, int(ceil(PAR * NP)) |
85
|
|
|
self.NP2 = int(NP - self.NP1) |
86
|
|
|
if ukwargs: |
87
|
|
|
logger.info('Unused arguments: %s' % (ukwargs)) |
88
|
|
|
|
89
|
|
|
def repair(self, x, lower, upper): |
90
|
|
|
r"""Truncate exceeded dimensions to the limits. |
91
|
|
|
|
92
|
|
|
Args: |
93
|
|
|
x (numpy.ndarray): Individual to repair. |
94
|
|
|
lower (numpy.ndarray): Lower limits for dimensions. |
95
|
|
|
upper (numpy.ndarray): Upper limits for dimensions. |
96
|
|
|
|
97
|
|
|
Returns: |
98
|
|
|
numpy.ndarray: Repaired individual. |
99
|
|
|
""" |
100
|
|
|
ir = where(x < lower) |
101
|
|
|
x[ir] = lower[ir] |
102
|
|
|
ir = where(x > upper) |
103
|
|
|
x[ir] = upper[ir] |
104
|
|
|
return x |
105
|
|
|
|
106
|
|
|
def levy(self, step_size, D): |
107
|
|
|
r"""Calculate levy flight. |
108
|
|
|
|
109
|
|
|
Args: |
110
|
|
|
step_size (float): Size of the walk step. |
111
|
|
|
D (int): Number of dimensions. |
112
|
|
|
|
113
|
|
|
Returns: |
114
|
|
|
numpy.ndarray: Calculated values for levy flight. |
115
|
|
|
""" |
116
|
|
|
delataX = np.array( |
117
|
|
|
[sum(np.tan(pi * self.uniform(0.0, 1.0, 10))) for _ in range(0, D)]) |
118
|
|
|
return delataX |
119
|
|
|
|
120
|
|
|
def migrationOperator(self, D, NP1, NP2, Butterflies): |
121
|
|
|
r"""Apply the migration operator. |
122
|
|
|
|
123
|
|
|
Args: |
124
|
|
|
D (int): Number of dimensions. |
125
|
|
|
NP1 (int): Number of butterflies in Land 1. |
126
|
|
|
NP2 (int): Number of butterflies in Land 2. |
127
|
|
|
Butterflies (numpy.ndarray): Current butterfly population. |
128
|
|
|
|
129
|
|
|
Returns: |
130
|
|
|
numpy.ndarray: Adjusted butterfly population. |
131
|
|
|
""" |
132
|
|
|
pop1 = np.copy(Butterflies[:NP1]) |
133
|
|
|
pop2 = np.copy(Butterflies[NP1:]) |
134
|
|
|
for k1 in range(0, NP1): |
135
|
|
|
for parnum1 in range(0, D): |
136
|
|
|
r1 = self.uniform(0.0, 1.0) * self.PER |
137
|
|
|
if r1 <= self.PAR: |
138
|
|
|
r2 = self.randint(Nmin=0, Nmax=NP1 - 1) |
139
|
|
|
Butterflies[k1, parnum1] = pop1[r2, parnum1] |
140
|
|
|
else: |
141
|
|
|
r3 = self.randint(Nmin=0, Nmax=NP2 - 1) |
142
|
|
|
Butterflies[k1, parnum1] = pop2[r3, parnum1] |
143
|
|
|
return Butterflies |
144
|
|
|
|
145
|
|
|
def adjustingOperator(self, t, max_t, D, NP1, NP2, Butterflies, best): |
146
|
|
|
r"""Apply the adjusting operator. |
147
|
|
|
|
148
|
|
|
Args: |
149
|
|
|
t (int): Current generation. |
150
|
|
|
max_t (int): Maximum generation. |
151
|
|
|
D (int): Number of dimensions. |
152
|
|
|
NP1 (int): Number of butterflies in Land 1. |
153
|
|
|
NP2 (int): Number of butterflies in Land 2. |
154
|
|
|
Butterflies (numpy.ndarray): Current butterfly population. |
155
|
|
|
best (numpy.ndarray): The best butterfly currently. |
156
|
|
|
|
157
|
|
|
Returns: |
158
|
|
|
numpy.ndarray: Adjusted butterfly population. |
159
|
|
|
""" |
160
|
|
|
pop2 = np.copy(Butterflies[NP1:]) |
161
|
|
|
for k2 in range(NP1, NP1 + NP2): |
162
|
|
|
scale = 1.0 / ((t + 1)**2) |
163
|
|
|
step_size = ceil(exponential(2 * max_t)) |
164
|
|
|
delataX = self.levy(step_size, D) |
165
|
|
|
for parnum2 in range(0, D): |
166
|
|
|
if self.uniform(0.0, 1.0) >= self.PAR: |
167
|
|
|
Butterflies[k2, parnum2] = best[parnum2] |
168
|
|
|
else: |
169
|
|
|
r4 = self.randint(Nmin=0, Nmax=NP2 - 1) |
170
|
|
|
Butterflies[k2, parnum2] = pop2[r4, 1] |
171
|
|
|
if(self.uniform(0.0, 1.0) > self.BAR): |
172
|
|
|
Butterflies[k2, parnum2] += scale * \ |
173
|
|
|
(delataX[parnum2] - 0.5) |
174
|
|
|
return Butterflies |
175
|
|
|
|
176
|
|
|
def evaluateAndSort(self, task, Butterflies): |
177
|
|
|
r"""Evaluate and sort the butterfly population. |
178
|
|
|
|
179
|
|
|
Args: |
180
|
|
|
task (Task): Optimization task |
181
|
|
|
Butterflies (numpy.ndarray): Current butterfly population. |
182
|
|
|
|
183
|
|
|
Returns: |
184
|
|
|
numpy.ndarray: Tuple[numpy.ndarray, float, numpy.ndarray]: |
185
|
|
|
1. Best butterfly according to the evaluation. |
186
|
|
|
2. The best fitness value. |
187
|
|
|
3. Butterfly population. |
188
|
|
|
""" |
189
|
|
|
Fitness = apply_along_axis(task.eval, 1, Butterflies) |
190
|
|
|
indices = argsort(Fitness) |
191
|
|
|
Butterflies = Butterflies[indices] |
192
|
|
|
Fitness = Fitness[indices] |
193
|
|
|
|
194
|
|
|
return Fitness, Butterflies |
195
|
|
|
|
196
|
|
|
def initPopulation(self, task): |
197
|
|
|
r"""Initialize the starting population. |
198
|
|
|
|
199
|
|
|
Args: |
200
|
|
|
task (Task): Optimization task |
201
|
|
|
|
202
|
|
|
Returns: |
203
|
|
|
Tuple[numpy.ndarray, numpy.ndarray[float], Dict[str, Any]]: |
204
|
|
|
1. New population. |
205
|
|
|
2. New population fitness/function values. |
206
|
|
|
3. Additional arguments: |
207
|
|
|
* dx (float): A small value used in local seeding stage. |
208
|
|
|
|
209
|
|
|
See Also: |
210
|
|
|
* :func:`NiaPy.algorithms.Algorithm.initPopulation` |
211
|
|
|
""" |
212
|
|
|
Butterflies = self.uniform(task.Lower, task.Upper, [self.NP, task.D]) |
213
|
|
|
Fitness, Butterflies = self.evaluateAndSort(task, Butterflies) |
214
|
|
|
return Butterflies, Fitness, {'tmp_best': Butterflies[0]} |
215
|
|
|
|
216
|
|
|
def runIteration(self, task, Butterflies, Evaluations, xb, fxb, tmp_best, **dparams): |
217
|
|
|
r"""Core function of Forest Optimization Algorithm. |
218
|
|
|
|
219
|
|
|
Args: |
220
|
|
|
task (Task): Optimization task. |
221
|
|
|
Butterflies (numpy.ndarray): Current population. |
222
|
|
|
Evaluations (numpy.ndarray[float]): Current population function/fitness values. |
223
|
|
|
xb (numpy.ndarray): Global best individual. |
224
|
|
|
fxb (float): Global best individual fitness/function value. |
225
|
|
|
tmp_best (numpy.ndarray): Best individual currently. |
226
|
|
|
**dparams (Dict[str, Any]): Additional arguments. |
227
|
|
|
|
228
|
|
|
Returns: |
229
|
|
|
Tuple[numpy.ndarray, numpy.ndarray[float], Dict[str, Any]]: |
230
|
|
|
1. New population. |
231
|
|
|
2. New population fitness/function values. |
232
|
|
|
3. Additional arguments: |
233
|
|
|
* dx (float): A small value used in local seeding stage. |
234
|
|
|
""" |
235
|
|
|
tmpElite = np.copy(Butterflies[:self.keep]) |
236
|
|
|
max_t = task.nGEN if isinf(task.nGEN) is False else task.nFES / self.NP |
237
|
|
|
|
238
|
|
|
Butterflies = apply_along_axis(self.repair, 1, self.migrationOperator( |
239
|
|
|
task.D, self.NP1, self.NP2, Butterflies), task.Lower, task.Upper) |
240
|
|
|
Butterflies = apply_along_axis(self.repair, 1, self.adjustingOperator( |
241
|
|
|
task.Iters, max_t, task.D, self.NP1, self.NP2, Butterflies, tmp_best), task.Lower, task.Upper) |
242
|
|
|
Fitness, Butterflies = self.evaluateAndSort(task, Butterflies) |
243
|
|
|
tmp_best = Butterflies[0] |
244
|
|
|
Butterflies[-self.keep:] = tmpElite |
245
|
|
|
Fitness, Butterflies = self.evaluateAndSort(task, Butterflies) |
246
|
|
|
return Butterflies, Fitness, {'tmp_best': tmp_best} |
247
|
|
|
|