1
|
|
|
# encoding=utf8 |
2
|
|
|
import logging |
3
|
|
|
from math import ceil |
4
|
|
|
|
5
|
|
|
from numpy import argmin, argsort, log, sum, fmax, sqrt, full, exp, eye, diag, apply_along_axis, round, any, asarray, dot, random as rand, tile, inf, where, append |
6
|
|
|
from numpy.linalg import norm, cholesky as chol, eig, solve, lstsq |
7
|
|
|
|
8
|
|
|
from NiaPy.algorithms.algorithm import Algorithm, Individual, defaultIndividualInit |
9
|
|
|
from NiaPy.util.utility import objects2array |
10
|
|
|
|
11
|
|
|
logging.basicConfig() |
12
|
|
|
logger = logging.getLogger('NiaPy.algorithms.basic') |
13
|
|
|
logger.setLevel('INFO') |
14
|
|
|
|
15
|
|
|
__all__ = ['EvolutionStrategy1p1', 'EvolutionStrategyMp1', 'EvolutionStrategyMpL', 'EvolutionStrategyML', 'CovarianceMatrixAdaptionEvolutionStrategy'] |
16
|
|
|
|
17
|
|
|
class IndividualES(Individual): |
18
|
|
|
r"""Individual for Evolution Strategies. |
19
|
|
|
|
20
|
|
|
See Also: |
21
|
|
|
* :class:`NiaPy.algorithms.Individual` |
22
|
|
|
""" |
23
|
|
|
def __init__(self, **kwargs): |
24
|
|
|
r"""Initialize individual. |
25
|
|
|
|
26
|
|
|
Args: |
27
|
|
|
kwargs (Dict[str, Any]): Additional arguments. |
28
|
|
|
|
29
|
|
|
See Also: |
30
|
|
|
* :func:`NiaPy.algorithms.Individual.__init__` |
31
|
|
|
""" |
32
|
|
|
Individual.__init__(self, **kwargs) |
33
|
|
|
self.rho = kwargs.get('rho', 1) |
34
|
|
|
|
35
|
|
|
class EvolutionStrategy1p1(Algorithm): |
36
|
|
|
r"""Implementation of (1 + 1) evolution strategy algorithm. Uses just one individual. |
37
|
|
|
|
38
|
|
|
Algorithm: |
39
|
|
|
(1 + 1) Evolution Strategy Algorithm |
40
|
|
|
|
41
|
|
|
Date: |
42
|
|
|
2018 |
43
|
|
|
|
44
|
|
|
Authors: |
45
|
|
|
Klemen Berkovič |
46
|
|
|
|
47
|
|
|
License: |
48
|
|
|
MIT |
49
|
|
|
|
50
|
|
|
Reference URL: |
51
|
|
|
|
52
|
|
|
Reference paper: |
53
|
|
|
|
54
|
|
|
Attributes: |
55
|
|
|
Name (List[str]): List of strings representing algorithm names. |
56
|
|
|
mu (int): Number of parents. |
57
|
|
|
k (int): Number of iterations before checking and fixing rho. |
58
|
|
|
c_a (float): Search range amplification factor. |
59
|
|
|
c_r (float): Search range reduction factor. |
60
|
|
|
|
61
|
|
|
See Also: |
62
|
|
|
* :class:`NiaPy.algorithms.Algorithm` |
63
|
|
|
""" |
64
|
|
|
Name = ['EvolutionStrategy1p1', 'EvolutionStrategy(1+1)', 'ES(1+1)'] |
65
|
|
|
|
66
|
|
|
@staticmethod |
67
|
|
|
def algorithmInfo(): |
68
|
|
|
r"""Get algorithms information. |
69
|
|
|
|
70
|
|
|
Returns: |
71
|
|
|
str: Algorithm information. |
72
|
|
|
|
73
|
|
|
See Also: |
74
|
|
|
* :func:`NiaPy.algorithms.Algorithm.algorithmInfo` |
75
|
|
|
""" |
76
|
|
|
return r"""TODO""" |
77
|
|
|
|
78
|
|
View Code Duplication |
@staticmethod |
|
|
|
|
79
|
|
|
def typeParameters(): |
80
|
|
|
r"""Get dictionary with functions for checking values of parameters. |
81
|
|
|
|
82
|
|
|
Returns: |
83
|
|
|
Dict[str, Callable]: |
84
|
|
|
* mu (Callable[[int], bool]) |
85
|
|
|
* k (Callable[[int], bool]) |
86
|
|
|
* c_a (Callable[[Union[float, int]], bool]) |
87
|
|
|
* c_r (Callable[[Union[float, int]], bool]) |
88
|
|
|
* epsilon (Callable[[float], bool]) |
89
|
|
|
""" |
90
|
|
|
return { |
91
|
|
|
'mu': lambda x: isinstance(x, int) and x > 0, |
92
|
|
|
'k': lambda x: isinstance(x, int) and x > 0, |
93
|
|
|
'c_a': lambda x: isinstance(x, (float, int)) and x > 1, |
94
|
|
|
'c_r': lambda x: isinstance(x, (float, int)) and 0 < x < 1, |
95
|
|
|
'epsilon': lambda x: isinstance(x, float) and 0 < x < 1 |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
def setParameters(self, mu=1, k=10, c_a=1.1, c_r=0.5, epsilon=1e-20, **ukwargs): |
99
|
|
|
r"""Set the arguments of an algorithm. |
100
|
|
|
|
101
|
|
|
Arguments: |
102
|
|
|
mu (Optional[int]): Number of parents |
103
|
|
|
k (Optional[int]): Number of iterations before checking and fixing rho |
104
|
|
|
c_a (Optional[float]): Search range amplification factor |
105
|
|
|
c_r (Optional[float]): Search range reduction factor |
106
|
|
|
|
107
|
|
|
See Also: |
108
|
|
|
* :func:`NiaPy.algorithms.Algorithm.setParameters` |
109
|
|
|
""" |
110
|
|
|
Algorithm.setParameters(self, NP=mu, itype=ukwargs.pop('itype', IndividualES), **ukwargs) |
111
|
|
|
self.mu, self.k, self.c_a, self.c_r, self.epsilon = mu, k, c_a, c_r, epsilon |
112
|
|
|
|
113
|
|
|
def mutate(self, x, rho): |
114
|
|
|
r"""Mutate individual. |
115
|
|
|
|
116
|
|
|
Args: |
117
|
|
|
x (Individual): Current individual. |
118
|
|
|
rho (float): Current standard deviation. |
119
|
|
|
|
120
|
|
|
Returns: |
121
|
|
|
Individual: Mutated individual. |
122
|
|
|
""" |
123
|
|
|
return x + self.normal(0, rho, len(x)) |
124
|
|
|
|
125
|
|
|
def updateRho(self, rho, k): |
126
|
|
|
r"""Update standard deviation. |
127
|
|
|
|
128
|
|
|
Args: |
129
|
|
|
rho (float): Current standard deviation. |
130
|
|
|
k (int): Number of succesfull mutations. |
131
|
|
|
|
132
|
|
|
Returns: |
133
|
|
|
float: New standard deviation. |
134
|
|
|
""" |
135
|
|
|
phi = k / self.k |
136
|
|
|
if phi < 0.2: return self.c_r * rho if rho > self.epsilon else 1 |
137
|
|
|
elif phi > 0.2: return self.c_a * rho if rho > self.epsilon else 1 |
138
|
|
|
else: return rho |
139
|
|
|
|
140
|
|
|
def initPopulation(self, task): |
141
|
|
|
r"""Initialize starting individual. |
142
|
|
|
|
143
|
|
|
Args: |
144
|
|
|
task (Task): Optimization task. |
145
|
|
|
|
146
|
|
|
Returns: |
147
|
|
|
Tuple[Individual, float, Dict[str, Any]]: |
148
|
|
|
1, Initialized individual. |
149
|
|
|
2, Initialized individual fitness/function value. |
150
|
|
|
3. Additional arguments: |
151
|
|
|
* ki (int): Number of successful rho update. |
152
|
|
|
""" |
153
|
|
|
c, ki = IndividualES(task=task, rnd=self.Rand), 0 |
154
|
|
|
return c, c.f, {'ki': ki} |
155
|
|
|
|
156
|
|
|
def runIteration(self, task, c, fpop, xb, fxb, ki, **dparams): |
157
|
|
|
r"""Core function of EvolutionStrategy(1+1) algorithm. |
158
|
|
|
|
159
|
|
|
Args: |
160
|
|
|
task (Task): Optimization task. |
161
|
|
|
pop (Individual): Current position. |
162
|
|
|
fpop (float): Current position function/fitness value. |
163
|
|
|
xb (Individual): Global best position. |
164
|
|
|
fxb (float): Global best function/fitness value. |
165
|
|
|
ki (int): Number of successful updates before rho update. |
166
|
|
|
**dparams (Dict[str, Any]): Additional arguments. |
167
|
|
|
|
168
|
|
|
Returns: |
169
|
|
|
Tuple[Individual, float, Individual, float, Dict[str, Any]]: |
170
|
|
|
1, Initialized individual. |
171
|
|
|
2, Initialized individual fitness/function value. |
172
|
|
|
3. New global best solution. |
173
|
|
|
4. New global best soluitons fitness/objective value. |
174
|
|
|
5. Additional arguments: |
175
|
|
|
* ki (int): Number of successful rho update. |
176
|
|
|
""" |
177
|
|
|
if task.Iters % self.k == 0: c.rho, ki = self.updateRho(c.rho, ki), 0 |
178
|
|
|
cn = objects2array([task.repair(self.mutate(c.x, c.rho), self.Rand) for _i in range(self.mu)]) |
179
|
|
|
cn_f = asarray([task.eval(cn[i]) for i in range(len(cn))]) |
180
|
|
|
ib = argmin(cn_f) |
181
|
|
|
if cn_f[ib] < c.f: |
182
|
|
|
c.x, c.f, ki = cn[ib], cn_f[ib], ki + 1 |
183
|
|
|
if cn_f[ib] < fxb: xb, fxb = self.getBest(cn[ib], cn_f[ib], xb, fxb) |
184
|
|
|
return c, c.f, xb, fxb, {'ki': ki} |
185
|
|
|
|
186
|
|
|
class EvolutionStrategyMp1(EvolutionStrategy1p1): |
187
|
|
|
r"""Implementation of (mu + 1) evolution strategy algorithm. Algorithm creates mu mutants but into new generation goes only one individual. |
188
|
|
|
|
189
|
|
|
Algorithm: |
190
|
|
|
(:math:`\mu + 1`) Evolution Strategy Algorithm |
191
|
|
|
|
192
|
|
|
Date: |
193
|
|
|
2018 |
194
|
|
|
|
195
|
|
|
Authors: |
196
|
|
|
Klemen Berkovič |
197
|
|
|
|
198
|
|
|
License: |
199
|
|
|
MIT |
200
|
|
|
|
201
|
|
|
Reference URL: |
202
|
|
|
|
203
|
|
|
Reference paper: |
204
|
|
|
|
205
|
|
|
Attributes: |
206
|
|
|
Name (List[str]): List of strings representing algorithm names. |
207
|
|
|
|
208
|
|
|
See Also: |
209
|
|
|
* :class:`NiaPy.algorithms.basic.EvolutionStrategy1p1` |
210
|
|
|
""" |
211
|
|
|
Name = ['EvolutionStrategyMp1', 'EvolutionStrategy(mu+1)', 'ES(m+1)'] |
212
|
|
|
|
213
|
|
|
@staticmethod |
214
|
|
|
def algorithmInfo(): |
215
|
|
|
r"""Get algorithms information. |
216
|
|
|
|
217
|
|
|
Returns: |
218
|
|
|
str: Algorithm information. |
219
|
|
|
|
220
|
|
|
See Also: |
221
|
|
|
* :func:`NiaPy.algorithms.Algorithm.algorithmInfo` |
222
|
|
|
""" |
223
|
|
|
return r"""TODO""" |
224
|
|
|
|
225
|
|
|
def setParameters(self, **kwargs): |
226
|
|
|
r"""Set core parameters of EvolutionStrategy(mu+1) algorithm. |
227
|
|
|
|
228
|
|
|
Args: |
229
|
|
|
**kwargs (Dict[str, Any]): |
230
|
|
|
|
231
|
|
|
See Also: |
232
|
|
|
* :func:`NiaPy.algorithms.basic.EvolutionStrategy1p1.setParameters` |
233
|
|
|
""" |
234
|
|
|
mu = kwargs.pop('mu', 40) |
235
|
|
|
EvolutionStrategy1p1.setParameters(self, mu=mu, **kwargs) |
236
|
|
|
|
237
|
|
|
class EvolutionStrategyMpL(EvolutionStrategy1p1): |
238
|
|
|
r"""Implementation of (mu + lambda) evolution strategy algorithm. Mulation creates lambda individual. Lambda individual compete with mu individuals for survival, so only mu individual go to new generation. |
239
|
|
|
|
240
|
|
|
Algorithm: |
241
|
|
|
(:math:`\mu + \lambda`) Evolution Strategy Algorithm |
242
|
|
|
|
243
|
|
|
Date: |
244
|
|
|
2018 |
245
|
|
|
|
246
|
|
|
Authors: |
247
|
|
|
Klemen Berkovič |
248
|
|
|
|
249
|
|
|
License: |
250
|
|
|
MIT |
251
|
|
|
|
252
|
|
|
Reference URL: |
253
|
|
|
|
254
|
|
|
Reference paper: |
255
|
|
|
|
256
|
|
|
Attributes: |
257
|
|
|
Name (List[str]): List of strings representing algorithm names |
258
|
|
|
lam (int): TODO |
259
|
|
|
|
260
|
|
|
See Also: |
261
|
|
|
* :class:`NiaPy.algorithms.basic.EvolutionStrategy1p1` |
262
|
|
|
""" |
263
|
|
|
Name = ['EvolutionStrategyMpL', 'EvolutionStrategy(mu+lambda)', 'ES(m+l)'] |
264
|
|
|
|
265
|
|
|
@staticmethod |
266
|
|
|
def algorithmInfo(): |
267
|
|
|
r"""Get algorithms information. |
268
|
|
|
|
269
|
|
|
Returns: |
270
|
|
|
str: Algorithm information. |
271
|
|
|
|
272
|
|
|
See Also: |
273
|
|
|
* :func:`NiaPy.algorithms.Algorithm.algorithmInfo` |
274
|
|
|
""" |
275
|
|
|
return r"""TODO""" |
276
|
|
|
|
277
|
|
|
@staticmethod |
278
|
|
|
def typeParameters(): |
279
|
|
|
r"""TODO. |
280
|
|
|
|
281
|
|
|
Returns: |
282
|
|
|
Dict[str, Any]: |
283
|
|
|
* lam (Callable[[int], bool]): TODO. |
284
|
|
|
|
285
|
|
|
See Also: |
286
|
|
|
* :func:`NiaPy.algorithms.basic.EvolutionStrategy1p1` |
287
|
|
|
""" |
288
|
|
|
d = EvolutionStrategy1p1.typeParameters() |
289
|
|
|
d['lam'] = lambda x: isinstance(x, int) and x > 0 |
290
|
|
|
return d |
291
|
|
|
|
292
|
|
|
def setParameters(self, lam=45, **ukwargs): |
293
|
|
|
r"""Set the arguments of an algorithm. |
294
|
|
|
|
295
|
|
|
Arguments: |
296
|
|
|
lam (int): Number of new individual generated by mutation. |
297
|
|
|
|
298
|
|
|
See Also: |
299
|
|
|
* :func:`NiaPy.algorithms.basic.es.EvolutionStrategy1p1.setParameters` |
300
|
|
|
""" |
301
|
|
|
EvolutionStrategy1p1.setParameters(self, InitPopFunc=defaultIndividualInit, **ukwargs) |
302
|
|
|
self.lam = lam |
303
|
|
|
|
304
|
|
|
def updateRho(self, pop, k): |
305
|
|
|
r"""Update standard deviation for population. |
306
|
|
|
|
307
|
|
|
Args: |
308
|
|
|
pop (numpy.ndarray[Individual]): Current population. |
309
|
|
|
k (int): Number of successful mutations. |
310
|
|
|
""" |
311
|
|
|
phi = k / self.k |
312
|
|
|
if phi < 0.2: |
313
|
|
|
for i in pop: i.rho = self.c_r * i.rho |
314
|
|
|
elif phi > 0.2: |
315
|
|
|
for i in pop: i.rho = self.c_a * i.rho |
316
|
|
|
|
317
|
|
|
def changeCount(self, c, cn): |
318
|
|
|
r"""Update number of successful mutations for population. |
319
|
|
|
|
320
|
|
|
Args: |
321
|
|
|
c (numpy.ndarray[Individual]): Current population. |
322
|
|
|
cn (numpy.ndarray[Individual]): New population. |
323
|
|
|
|
324
|
|
|
Returns: |
325
|
|
|
int: Number of successful mutations. |
326
|
|
|
""" |
327
|
|
|
k = 0 |
328
|
|
|
for e in cn: |
329
|
|
|
if e not in c: k += 1 |
330
|
|
|
return k |
331
|
|
|
|
332
|
|
|
def mutateRand(self, pop, task): |
333
|
|
|
r"""Mutate random individual form population. |
334
|
|
|
|
335
|
|
|
Args: |
336
|
|
|
pop (numpy.ndarray[Individual]): Current population. |
337
|
|
|
task (Task): Optimization task. |
338
|
|
|
|
339
|
|
|
Returns: |
340
|
|
|
numpy.ndarray: Random individual from population that was mutated. |
341
|
|
|
""" |
342
|
|
|
i = self.randint(self.mu) |
343
|
|
|
return task.repair(self.mutate(pop[i].x, pop[i].rho), rnd=self.Rand) |
344
|
|
|
|
345
|
|
|
def initPopulation(self, task): |
346
|
|
|
r"""Initialize starting population. |
347
|
|
|
|
348
|
|
|
Args: |
349
|
|
|
task (Task): Optimization task. |
350
|
|
|
|
351
|
|
|
Returns: |
352
|
|
|
Tuple[numpy.ndarray[Individual], numpy.ndarray[float], Dict[str, Any]]: |
353
|
|
|
1. Initialized populaiton. |
354
|
|
|
2. Initialized populations function/fitness values. |
355
|
|
|
3. Additional arguments: |
356
|
|
|
* ki (int): Number of successful mutations. |
357
|
|
|
|
358
|
|
|
See Also: |
359
|
|
|
* :func:`NiaPy.algorithms.algorithm.Algorithm.initPopulation` |
360
|
|
|
""" |
361
|
|
|
c, fc, d = Algorithm.initPopulation(self, task) |
362
|
|
|
d.update({'ki': 0}) |
363
|
|
|
return c, fc, d |
364
|
|
|
|
365
|
|
|
def runIteration(self, task, c, fpop, xb, fxb, ki, **dparams): |
366
|
|
|
r"""Core function of EvolutionStrategyMpL algorithm. |
367
|
|
|
|
368
|
|
|
Args: |
369
|
|
|
task (Task): Optimization task. |
370
|
|
|
c (numpy.ndarray): Current population. |
371
|
|
|
fpop (numpy.ndarray): Current populations fitness/function values. |
372
|
|
|
xb (numpy.ndarray): Global best individual. |
373
|
|
|
fxb (float): Global best individuals fitness/function value. |
374
|
|
|
ki (int): Number of successful mutations. |
375
|
|
|
**dparams (Dict[str, Any]): Additional arguments. |
376
|
|
|
|
377
|
|
|
Returns: |
378
|
|
|
Tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray, float, Dict[str, Any]]: |
379
|
|
|
1. New population. |
380
|
|
|
2. New populations function/fitness values. |
381
|
|
|
3. New global best solution. |
382
|
|
|
4. New global best solutions fitness/objective value. |
383
|
|
|
5. Additional arguments: |
384
|
|
|
* ki (int): Number of successful mutations. |
385
|
|
|
""" |
386
|
|
|
if task.Iters % self.k == 0: _, ki = self.updateRho(c, ki), 0 |
387
|
|
|
cn = objects2array([IndividualES(x=self.mutateRand(c, task), task=task, rnd=self.Rand) for _ in range(self.lam)]) |
388
|
|
|
cn = append(cn, c) |
389
|
|
|
cn = objects2array([cn[i] for i in argsort([i.f for i in cn])[:self.mu]]) |
390
|
|
|
ki += self.changeCount(c, cn) |
391
|
|
|
fcn = asarray([x.f for x in cn]) |
392
|
|
|
xb, fxb = self.getBest(cn, fcn, xb, fxb) |
393
|
|
|
return cn, fcn, xb, fxb, {'ki': ki} |
394
|
|
|
|
395
|
|
|
class EvolutionStrategyML(EvolutionStrategyMpL): |
396
|
|
|
r"""Implementation of (mu, lambda) evolution strategy algorithm. Algorithm is good for dynamic environments. Mu individual create lambda chields. Only best mu chields go to new generation. Mu parents are discarded. |
397
|
|
|
|
398
|
|
|
Algorithm: |
399
|
|
|
(:math:`\mu + \lambda`) Evolution Strategy Algorithm |
400
|
|
|
|
401
|
|
|
Date: |
402
|
|
|
2018 |
403
|
|
|
|
404
|
|
|
Authors: |
405
|
|
|
Klemen Berkovič |
406
|
|
|
|
407
|
|
|
License: |
408
|
|
|
MIT |
409
|
|
|
|
410
|
|
|
Reference URL: |
411
|
|
|
|
412
|
|
|
Reference paper: |
413
|
|
|
|
414
|
|
|
Attributes: |
415
|
|
|
Name (List[str]): List of strings representing algorithm names |
416
|
|
|
|
417
|
|
|
See Also: |
418
|
|
|
* :class:`NiaPy.algorithm.basic.es.EvolutionStrategyMpL` |
419
|
|
|
""" |
420
|
|
|
Name = ['EvolutionStrategyML', 'EvolutionStrategy(mu,lambda)', 'ES(m,l)'] |
421
|
|
|
|
422
|
|
|
@staticmethod |
423
|
|
|
def algorithmInfo(): |
424
|
|
|
r"""Get algorithms information. |
425
|
|
|
|
426
|
|
|
Returns: |
427
|
|
|
str: Algorithm information. |
428
|
|
|
|
429
|
|
|
See Also: |
430
|
|
|
* :func:`NiaPy.algorithms.Algorithm.algorithmInfo` |
431
|
|
|
""" |
432
|
|
|
return r"""TODO""" |
433
|
|
|
|
434
|
|
|
def newPop(self, pop): |
435
|
|
|
r"""Return new population. |
436
|
|
|
|
437
|
|
|
Args: |
438
|
|
|
pop (numpy.ndarray): Current population. |
439
|
|
|
|
440
|
|
|
Returns: |
441
|
|
|
numpy.ndarray: New population. |
442
|
|
|
""" |
443
|
|
|
pop_s = argsort([i.f for i in pop]) |
444
|
|
|
if self.mu < self.lam: return objects2array([pop[i] for i in pop_s[:self.mu]]) |
445
|
|
|
npop = list() |
446
|
|
|
for i in range(int(ceil(float(self.mu) / self.lam))): npop.extend(pop[:self.lam if (self.mu - i * self.lam) >= self.lam else self.mu - i * self.lam]) |
447
|
|
|
return objects2array(npop) |
448
|
|
|
|
449
|
|
|
def initPopulation(self, task): |
450
|
|
|
r"""Initialize starting population. |
451
|
|
|
|
452
|
|
|
Args: |
453
|
|
|
task (Task): Optimization task. |
454
|
|
|
|
455
|
|
|
Returns: |
456
|
|
|
Tuple[numpy.ndarray[Individual], numpy.ndarray[float], Dict[str, Any]]: |
457
|
|
|
1. Initialized population. |
458
|
|
|
2. Initialized populations fitness/function values. |
459
|
|
|
2. Additional arguments. |
460
|
|
|
|
461
|
|
|
See Also: |
462
|
|
|
* :func:`NiaPy.algorithm.basic.es.EvolutionStrategyMpL.initPopulation` |
463
|
|
|
""" |
464
|
|
|
c, fc, _ = EvolutionStrategyMpL.initPopulation(self, task) |
465
|
|
|
return c, fc, {} |
466
|
|
|
|
467
|
|
|
def runIteration(self, task, c, fpop, xb, fxb, **dparams): |
468
|
|
|
r"""Core function of EvolutionStrategyML algorithm. |
469
|
|
|
|
470
|
|
|
Args: |
471
|
|
|
task (Task): Optimization task. |
472
|
|
|
c (numpy.ndarray): Current population. |
473
|
|
|
fpop (numpy.ndarray): Current population fitness/function values. |
474
|
|
|
xb (numpy.ndarray): Global best individual. |
475
|
|
|
fxb (float): Global best individuals fitness/function value. |
476
|
|
|
**dparams Dict[str, Any]: Additional arguments. |
477
|
|
|
|
478
|
|
|
Returns: |
479
|
|
|
Tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray, float, Dict[str, Any]]: |
480
|
|
|
1. New population. |
481
|
|
|
2. New populations fitness/function values. |
482
|
|
|
3. New global best solution. |
483
|
|
|
4. New global best solutions fitness/objective value. |
484
|
|
|
5. Additional arguments. |
485
|
|
|
""" |
486
|
|
|
cn = objects2array([IndividualES(x=self.mutateRand(c, task), task=task, rand=self.Rand) for _ in range(self.lam)]) |
487
|
|
|
c = self.newPop(cn) |
488
|
|
|
fc = asarray([x.f for x in c]) |
489
|
|
|
xb, fxb = self.getBest(c, fc, xb, fxb) |
490
|
|
|
return c, fc, xb, fxb, {} |
491
|
|
|
|
492
|
|
|
def CovarianceMaatrixAdaptionEvolutionStrategyF(task, epsilon=1e-20, rnd=rand): |
493
|
|
|
lam, alpha_mu, hs, sigma0 = (4 + round(3 * log(task.D))) * 10, 2, 0, 0.3 * task.bcRange() |
494
|
|
|
mu = int(round(lam / 2)) |
495
|
|
|
w = log(mu + 0.5) - log(range(1, mu + 1)) |
496
|
|
|
w = w / sum(w) |
497
|
|
|
mueff = 1 / sum(w ** 2) |
498
|
|
|
cs = (mueff + 2) / (task.D + mueff + 5) |
499
|
|
|
ds = 1 + cs + 2 * max(sqrt((mueff - 1) / (task.D + 1)) - 1, 0) |
500
|
|
|
ENN = sqrt(task.D) * (1 - 1 / (4 * task.D) + 1 / (21 * task.D ** 2)) |
501
|
|
|
cc, c1 = (4 + mueff / task.D) / (4 + task.D + 2 * mueff / task.D), 2 / ((task.D + 1.3) ** 2 + mueff) |
502
|
|
|
cmu, hth = min(1 - c1, alpha_mu * (mueff - 2 + 1 / mueff) / ((task.D + 2) ** 2 + alpha_mu * mueff / 2)), (1.4 + 2 / (task.D + 1)) * ENN |
503
|
|
|
ps, pc, C, sigma, M = full(task.D, 0.0), full(task.D, 0.0), eye(task.D), sigma0, full(task.D, 0.0) |
504
|
|
|
x = rnd.uniform(task.bcLower(), task.bcUpper()) |
505
|
|
|
x_f = task.eval(x) |
506
|
|
|
while not task.stopCondI(): |
507
|
|
|
pop_step = asarray([rnd.multivariate_normal(full(task.D, 0.0), C) for _ in range(int(lam))]) |
508
|
|
|
pop = asarray([task.repair(x + sigma * ps, rnd) for ps in pop_step]) |
509
|
|
|
pop_f = apply_along_axis(task.eval, 1, pop) |
510
|
|
|
isort = argsort(pop_f) |
511
|
|
|
pop, pop_f, pop_step = pop[isort[:mu]], pop_f[isort[:mu]], pop_step[isort[:mu]] |
512
|
|
|
if pop_f[0] < x_f: x, x_f = pop[0], pop_f[0] |
513
|
|
|
M = sum(w * pop_step.T, axis=1) |
514
|
|
|
ps = solve(chol(C).conj() + epsilon, ((1 - cs) * ps + sqrt(cs * (2 - cs) * mueff) * M + epsilon).T)[0].T |
515
|
|
|
sigma *= exp(cs / ds * (norm(ps) / ENN - 1)) ** 0.3 |
516
|
|
|
ifix = where(sigma == inf) |
517
|
|
|
if any(ifix): sigma[ifix] = sigma0 |
518
|
|
|
if norm(ps) / sqrt(1 - (1 - cs) ** (2 * (task.Iters + 1))) < hth: hs = 1 |
519
|
|
|
else: hs = 0 |
520
|
|
|
delta = (1 - hs) * cc * (2 - cc) |
521
|
|
|
pc = (1 - cc) * pc + hs * sqrt(cc * (2 - cc) * mueff) * M |
522
|
|
|
C = (1 - c1 - cmu) * C + c1 * (tile(pc, [len(pc), 1]) * tile(pc.reshape([len(pc), 1]), [1, len(pc)]) + delta * C) |
523
|
|
|
for i in range(mu): C += cmu * w[i] * tile(pop_step[i], [len(pop_step[i]), 1]) * tile(pop_step[i].reshape([len(pop_step[i]), 1]), [1, len(pop_step[i])]) |
524
|
|
|
E, V = eig(C) |
525
|
|
|
if any(E < epsilon): |
526
|
|
|
E = fmax(E, 0) |
527
|
|
|
C = lstsq(V.T, dot(V, diag(E)).T, rcond=None)[0].T |
528
|
|
|
return x, x_f |
529
|
|
|
|
530
|
|
|
class CovarianceMatrixAdaptionEvolutionStrategy(Algorithm): |
531
|
|
|
r"""Implementation of (mu, lambda) evolution strategy algorithm. Algorithm is good for dynamic environments. Mu individual create lambda chields. Only best mu chields go to new generation. Mu parents are discarded. |
532
|
|
|
|
533
|
|
|
Algorithm: |
534
|
|
|
(:math:`\mu + \lambda`) Evolution Strategy Algorithm |
535
|
|
|
|
536
|
|
|
Date: |
537
|
|
|
2018 |
538
|
|
|
|
539
|
|
|
Authors: |
540
|
|
|
Klemen Berkovič |
541
|
|
|
|
542
|
|
|
License: |
543
|
|
|
MIT |
544
|
|
|
|
545
|
|
|
Reference URL: |
546
|
|
|
https://arxiv.org/abs/1604.00772 |
547
|
|
|
|
548
|
|
|
Reference paper: |
549
|
|
|
Hansen, Nikolaus. "The CMA evolution strategy: A tutorial." arXiv preprint arXiv:1604.00772 (2016). |
550
|
|
|
|
551
|
|
|
Attributes: |
552
|
|
|
Name (List[str]): List of names representing algorithm names |
553
|
|
|
epsilon (float): TODO |
554
|
|
|
""" |
555
|
|
|
Name = ['CovarianceMatrixAdaptionEvolutionStrategy', 'CMA-ES', 'CMAES'] |
556
|
|
|
epsilon = 1e-20 |
557
|
|
|
|
558
|
|
|
@staticmethod |
559
|
|
|
def algorithmInfo(): |
560
|
|
|
r"""Get algorithms information. |
561
|
|
|
|
562
|
|
|
Returns: |
563
|
|
|
str: Algorithm information. |
564
|
|
|
|
565
|
|
|
See Also: |
566
|
|
|
* :func:`NiaPy.algorithms.Algorithm.algorithmInfo` |
567
|
|
|
""" |
568
|
|
|
return r"""TODO""" |
569
|
|
|
|
570
|
|
|
@staticmethod |
571
|
|
|
def typeParameters(): return { |
572
|
|
|
'epsilon': lambda x: isinstance(x, (float, int)) and 0 < x < 1 |
573
|
|
|
} |
574
|
|
|
|
575
|
|
|
def setParameters(self, epsilon=1e-20, **ukwargs): |
576
|
|
|
r"""Set core parameters of CovarianceMatrixAdaptionEvolutionStrategy algorithm. |
577
|
|
|
|
578
|
|
|
Args: |
579
|
|
|
epsilon (float): Small number. |
580
|
|
|
**ukwargs (Dict[str, Any]): Additional arguments. |
581
|
|
|
""" |
582
|
|
|
Algorithm.setParameters(self, **ukwargs) |
583
|
|
|
self.epsilon = epsilon |
584
|
|
|
|
585
|
|
|
def runTask(self, task): |
586
|
|
|
r"""TODO. |
587
|
|
|
|
588
|
|
|
Args: |
589
|
|
|
task (Task): Optimization task. |
590
|
|
|
|
591
|
|
|
Returns: |
592
|
|
|
TODO. |
593
|
|
|
""" |
594
|
|
|
return CovarianceMaatrixAdaptionEvolutionStrategyF(task, self.epsilon, rnd=self.Rand) |
595
|
|
|
|
596
|
|
|
# vim: tabstop=3 noexpandtab shiftwidth=3 softtabstop=3 |
597
|
|
|
|