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