1
|
|
|
# encoding=utf8 |
2
|
|
|
# pylint: disable=line-too-long, mixed-indentation, bad-continuation,multiple-statements, unused-argument, no-self-use, trailing-comma-tuple, logging-not-lazy, no-else-return, dangerous-default-value, assignment-from-no-return, superfluous-parens |
3
|
|
|
|
4
|
|
|
"""Implementation of benchmarks utility function.""" |
5
|
|
|
import logging |
6
|
|
|
# from datetime import datetime |
7
|
|
|
from enum import Enum |
8
|
|
|
|
9
|
|
|
from numpy import ndarray, asarray, full, empty, inf, dot, where, random as rand, fabs, ceil, amin, amax |
10
|
|
|
from matplotlib import pyplot as plt, animation as anim |
11
|
|
|
|
12
|
|
|
from NiaPy.benchmarks import Rastrigin, Rosenbrock, Griewank, Sphere, Ackley, Schwefel, Schwefel221, Schwefel222, Whitley, Alpine1, Alpine2, HappyCat, Ridge, ChungReynolds, Csendes, Pinter, Qing, Quintic, Salomon, SchumerSteiglitz, Step, Step2, Step3, Stepint, SumSquares, StyblinskiTang, BentCigar, Discus, Elliptic, ExpandedGriewankPlusRosenbrock, HGBat, Katsuura, ExpandedSchaffer, ModifiedSchwefel, Weierstrass, Michalewichz, Levy, Sphere2, Sphere3, Trid, Perm, Zakharov, DixonPrice, Powell, CosineMixture, Infinity, SchafferN2, SchafferN4 |
13
|
|
|
from NiaPy.util.exception import FesException, GenException, RefException # TimeException, |
14
|
|
|
|
15
|
|
|
logging.basicConfig() |
16
|
|
|
logger = logging.getLogger('NiaPy.util.utility') |
17
|
|
|
logger.setLevel('INFO') |
18
|
|
|
|
19
|
|
|
__all__ = [ |
20
|
|
|
'Utility', |
21
|
|
|
'limitRepair', |
22
|
|
|
'limitInversRepair', |
23
|
|
|
'wangRepair', |
24
|
|
|
'randRepair', |
25
|
|
|
'Task', |
26
|
|
|
'CountingTask', |
27
|
|
|
'StoppingTask', |
28
|
|
|
'ThrowingTask', |
29
|
|
|
'TaskConvPrint', |
30
|
|
|
'TaskConvPlot', |
31
|
|
|
'TaskConvSave', |
32
|
|
|
'fullArray', |
33
|
|
|
'objects2array', |
34
|
|
|
'TaskComposition', |
35
|
|
|
'OptimizationType', |
36
|
|
|
'ScaledTask' |
37
|
|
|
] |
38
|
|
|
|
39
|
|
|
def fullArray(a, D): |
40
|
|
|
r"""Fill or create array of length D, from value or value form a. |
41
|
|
|
|
42
|
|
|
Arguments: |
43
|
|
|
a (Union[int, float, numpy.ndarray], Iterable[Any]): Input values for fill. |
44
|
|
|
D (int): Length of new array. |
45
|
|
|
|
46
|
|
|
Returns: |
47
|
|
|
numpy.ndarray: Array filled with passed values or value. |
48
|
|
|
""" |
49
|
|
|
A = [] |
50
|
|
|
if isinstance(a, (int, float)): A = full(D, a) |
51
|
|
|
elif isinstance(a, (ndarray, list, tuple)): |
52
|
|
|
if len(a) == D: A = a if isinstance(a, ndarray) else asarray(a) |
53
|
|
|
elif len(a) > D: A = a[:D] if isinstance(a, ndarray) else asarray(a[:D]) |
54
|
|
|
else: |
55
|
|
|
for i in range(int(ceil(float(D) / len(a)))): A.extend(a[:D if (D - i * len(a)) >= len(a) else D - i * len(a)]) |
56
|
|
|
A = asarray(A) |
57
|
|
|
return A |
58
|
|
|
|
59
|
|
|
def objects2array(objs): |
60
|
|
|
r"""Convert `Iterable` array or list to `NumPy` array. |
61
|
|
|
|
62
|
|
|
Args: |
63
|
|
|
objs (Iterable[Any]): Array or list to convert. |
64
|
|
|
|
65
|
|
|
Returns: |
66
|
|
|
numpy.ndarray: Array of objects. |
67
|
|
|
""" |
68
|
|
|
a = empty(len(objs), dtype=object) |
69
|
|
|
for i, e in enumerate(objs): a[i] = e |
70
|
|
|
return a |
71
|
|
|
|
72
|
|
|
class Utility: |
73
|
|
|
r"""Base class with string mappings to benchmarks. |
74
|
|
|
|
75
|
|
|
Attributes: |
76
|
|
|
classes (Dict[str, Benchmark]): Mapping from stings to benchmark. |
77
|
|
|
""" |
78
|
|
|
def __init__(self): |
79
|
|
|
r""" |
80
|
|
|
|
81
|
|
|
""" |
82
|
|
|
self.classes = { |
83
|
|
|
'ackley': Ackley, |
84
|
|
|
'alpine1': Alpine1, |
85
|
|
|
'alpine2': Alpine2, |
86
|
|
|
'bentcigar': BentCigar, |
87
|
|
|
'chungReynolds': ChungReynolds, |
88
|
|
|
'cosinemixture': CosineMixture, |
89
|
|
|
'csendes': Csendes, |
90
|
|
|
'discus': Discus, |
91
|
|
|
'dixonprice': DixonPrice, |
92
|
|
|
'conditionedellptic': Elliptic, |
93
|
|
|
'elliptic': Elliptic, |
94
|
|
|
'expandedgriewankplusrosenbrock': ExpandedGriewankPlusRosenbrock, |
95
|
|
|
'expandedschaffer': ExpandedSchaffer, |
96
|
|
|
'griewank': Griewank, |
97
|
|
|
'happyCat': HappyCat, |
98
|
|
|
'hgbat': HGBat, |
99
|
|
|
'infinity': Infinity, |
100
|
|
|
'katsuura': Katsuura, |
101
|
|
|
'levy': Levy, |
102
|
|
|
'michalewicz': Michalewichz, |
103
|
|
|
'modifiedscwefel': ModifiedSchwefel, |
104
|
|
|
'perm': Perm, |
105
|
|
|
'pinter': Pinter, |
106
|
|
|
'powell': Powell, |
107
|
|
|
'qing': Qing, |
108
|
|
|
'quintic': Quintic, |
109
|
|
|
'rastrigin': Rastrigin, |
110
|
|
|
'ridge': Ridge, |
111
|
|
|
'rosenbrock': Rosenbrock, |
112
|
|
|
'salomon': Salomon, |
113
|
|
|
'schaffer2': SchafferN2, |
114
|
|
|
'schaffer4': SchafferN4, |
115
|
|
|
'schumerSteiglitz': SchumerSteiglitz, |
116
|
|
|
'schwefel': Schwefel, |
117
|
|
|
'schwefel221': Schwefel221, |
118
|
|
|
'schwefel222': Schwefel222, |
119
|
|
|
'sphere': Sphere, |
120
|
|
|
'sphere2': Sphere2, |
121
|
|
|
'sphere3': Sphere3, |
122
|
|
|
'step': Step, |
123
|
|
|
'step2': Step2, |
124
|
|
|
'step3': Step3, |
125
|
|
|
'stepint': Stepint, |
126
|
|
|
'styblinskiTang': StyblinskiTang, |
127
|
|
|
'sumSquares': SumSquares, |
128
|
|
|
'trid': Trid, |
129
|
|
|
'weierstrass': Weierstrass, |
130
|
|
|
'whitley': Whitley, |
131
|
|
|
'zakharov': Zakharov |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
def get_benchmark(self, benchmark): |
135
|
|
|
r"""Get the optimization problem. |
136
|
|
|
|
137
|
|
|
Arguments: |
138
|
|
|
benchmark (Union[str, Benchmark]): String or class that represents the optimization problem. |
139
|
|
|
|
140
|
|
|
Returns: |
141
|
|
|
Benchmark: Optimization function with limits. |
142
|
|
|
""" |
143
|
|
|
if not isinstance(benchmark, str) and not callable(benchmark): return benchmark |
144
|
|
|
elif benchmark in self.classes: return self.classes[benchmark]() |
145
|
|
|
else: raise TypeError('Passed benchmark is not defined!') |
146
|
|
|
|
147
|
|
|
@classmethod |
148
|
|
|
def __raiseLowerAndUpperNotDefined(cls): |
149
|
|
|
r"""Trow exception if lower and upper bounds are not defined in benchmark. |
150
|
|
|
|
151
|
|
|
Raises: |
152
|
|
|
TypeError: Type error. |
153
|
|
|
""" |
154
|
|
|
raise TypeError('Upper and Lower value must be defined!') |
155
|
|
|
|
156
|
|
|
class OptimizationType(Enum): |
157
|
|
|
r"""Enum representing type of optimization. |
158
|
|
|
|
159
|
|
|
Attributes: |
160
|
|
|
MINIMIZATION (int): Represents minimization problems and is default optimization type of all algorithms. |
161
|
|
|
MAXIMIZATION (int): Represents maximization problems. |
162
|
|
|
""" |
163
|
|
|
MINIMIZATION = 1.0 |
164
|
|
|
MAXIMIZATION = -1.0 |
165
|
|
|
|
166
|
|
|
def limitRepair(x, Lower, Upper, **kwargs): |
167
|
|
|
r"""Repair solution and put the solution in the random position inside of the bounds of problem. |
168
|
|
|
|
169
|
|
|
Arguments: |
170
|
|
|
x (numpy.ndarray): Solution to check and repair if needed. |
171
|
|
|
Lower (numpy.ndarray): Lower bounds of search space. |
172
|
|
|
Upper (numpy.ndarray): Upper bounds of search space. |
173
|
|
|
kwargs (Dict[str, Any]): Additional arguments. |
174
|
|
|
|
175
|
|
|
Returns: |
176
|
|
|
numpy.ndarray: Solution in search space. |
177
|
|
|
""" |
178
|
|
|
ir = where(x < Lower) |
179
|
|
|
x[ir] = Lower[ir] |
180
|
|
|
ir = where(x > Upper) |
181
|
|
|
x[ir] = Lower[ir] |
182
|
|
|
return x |
183
|
|
|
|
184
|
|
|
def limitInversRepair(x, Lower, Upper, **kwargs): |
185
|
|
|
r"""Repair solution and put the solution in the random position inside of the bounds of problem. |
186
|
|
|
|
187
|
|
|
Arguments: |
188
|
|
|
x (numpy.ndarray): Solution to check and repair if needed. |
189
|
|
|
Lower (numpy.ndarray): Lower bounds of search space. |
190
|
|
|
Upper (numpy.ndarray): Upper bounds of search space. |
191
|
|
|
kwargs (Dict[str, Any]): Additional arguments. |
192
|
|
|
|
193
|
|
|
Returns: |
194
|
|
|
numpy.ndarray: Solution in search space. |
195
|
|
|
""" |
196
|
|
|
ir = where(x < Lower) |
197
|
|
|
x[ir] = Upper[ir] |
198
|
|
|
ir = where(x > Upper) |
199
|
|
|
x[ir] = Lower[ir] |
200
|
|
|
return x |
201
|
|
|
|
202
|
|
|
def wangRepair(x, Lower, Upper, **kwargs): |
203
|
|
|
r"""Repair solution and put the solution in the random position inside of the bounds of problem. |
204
|
|
|
|
205
|
|
|
Arguments: |
206
|
|
|
x (numpy.ndarray): Solution to check and repair if needed. |
207
|
|
|
Lower (numpy.ndarray): Lower bounds of search space. |
208
|
|
|
Upper (numpy.ndarray): Upper bounds of search space. |
209
|
|
|
kwargs (Dict[str, Any]): Additional arguments. |
210
|
|
|
|
211
|
|
|
Returns: |
212
|
|
|
numpy.ndarray: Solution in search space. |
213
|
|
|
""" |
214
|
|
|
ir = where(x < Lower) |
215
|
|
|
x[ir] = amin([Upper[ir], 2 * Lower[ir] - x[ir]], axis=0) |
216
|
|
|
ir = where(x > Upper) |
217
|
|
|
x[ir] = amax([Lower[ir], 2 * Upper[ir] - x[ir]], axis=0) |
218
|
|
|
return x |
219
|
|
|
|
220
|
|
|
def randRepair(x, Lower, Upper, rnd=rand, **kwargs): |
221
|
|
|
r"""Repair solution and put the solution in the random position inside of the bounds of problem. |
222
|
|
|
|
223
|
|
|
Arguments: |
224
|
|
|
x (array): Solution to check and repair if needed. |
225
|
|
|
Lower (numpy.ndarray): Lower bounds of search space. |
226
|
|
|
Upper (numpy.ndarray): Upper bounds of search space. |
227
|
|
|
rnd (mtrand.RandomState): Random generator. |
228
|
|
|
kwargs (Dict[str, Any]): Additional arguments. |
229
|
|
|
|
230
|
|
|
Returns: |
231
|
|
|
numpy.ndarray: Fixed solution. |
232
|
|
|
""" |
233
|
|
|
ir = where(x < Lower) |
234
|
|
|
x[ir] = rnd.uniform(Lower[ir], Upper[ir]) |
235
|
|
|
ir = where(x > Upper) |
236
|
|
|
x[ir] = rnd.uniform(Lower[ir], Upper[ir]) |
237
|
|
|
return x |
238
|
|
|
|
239
|
|
|
def reflectRepair(x, Lower, Upper, **kwargs): |
240
|
|
|
r"""Repair solution and put the solution in search space with reflection of how much the solution violates a bound. |
241
|
|
|
|
242
|
|
|
Args: |
243
|
|
|
x (numpy.ndarray): Solution to be fixed. |
244
|
|
|
Lower (numpy.ndarray): Lower bounds of search space. |
245
|
|
|
Upper (numpy.ndarray): Upper bounds of search space. |
246
|
|
|
kwargs (Dict[str, Any]): Additional arguments. |
247
|
|
|
|
248
|
|
|
Returns: |
249
|
|
|
numpy.ndarray: Fix solution. |
250
|
|
|
""" |
251
|
|
|
ir = where(x > Upper) |
252
|
|
|
x[ir] = Lower[ir] + x[ir] % (Upper[ir] - Lower[ir]) |
253
|
|
|
ir = where(x < Lower) |
254
|
|
|
x[ir] = Lower[ir] + x[ir] % (Upper[ir] - Lower[ir]) |
255
|
|
|
return x |
256
|
|
|
|
257
|
|
|
class Task(Utility): |
258
|
|
|
r"""Class representing problem to solve with optimization. |
259
|
|
|
|
260
|
|
|
Date: |
261
|
|
|
2019 |
262
|
|
|
|
263
|
|
|
Author: |
264
|
|
|
Klemen Berkovič |
265
|
|
|
|
266
|
|
|
Attributes: |
267
|
|
|
D (int): Dimension of the problem. |
268
|
|
|
Lower (numpy.ndarray): Lower bounds of the problem. |
269
|
|
|
Upper (numpy.ndarray): Upper bounds of the problem. |
270
|
|
|
bRange (numpy.ndarray): Search range between upper and lower limits. |
271
|
|
|
optType (OptimizationType): Optimization type to use. |
272
|
|
|
|
273
|
|
|
See Also: |
274
|
|
|
* :class:`NiaPy.util.Utility` |
275
|
|
|
""" |
276
|
|
|
D = 0 |
277
|
|
|
benchmark = None |
278
|
|
|
Lower, Upper, bRange = inf, inf, inf |
279
|
|
|
optType = OptimizationType.MINIMIZATION |
280
|
|
|
|
281
|
|
|
def __init__(self, D=0, optType=OptimizationType.MINIMIZATION, benchmark=None, Lower=None, Upper=None, frepair=limitRepair, **kwargs): |
282
|
|
|
r"""Initialize task class for optimization. |
283
|
|
|
|
284
|
|
|
Arguments: |
285
|
|
|
D (Optional[int]): Number of dimensions. |
286
|
|
|
optType (Optional[OptimizationType]): Set the type of optimization. |
287
|
|
|
benchmark (Union[str, Benchmark]): Problem to solve with optimization. |
288
|
|
|
Lower (Optional[numpy.ndarray]): Lower limits of the problem. |
289
|
|
|
Upper (Optional[numpy.ndarray]): Upper limits of the problem. |
290
|
|
|
frepair (Optional[Callable[[numpy.ndarray, numpy.ndarray, numpy.ndarray, Dict[str, Any]], numpy.ndarray]]): Function for reparing individuals components to desired limits. |
291
|
|
|
|
292
|
|
|
See Also: |
293
|
|
|
* `func`:NiaPy.util.Utility.__init__` |
294
|
|
|
* `func`:NiaPy.util.Utility.repair` |
295
|
|
|
""" |
296
|
|
|
Utility.__init__(self) |
297
|
|
|
# dimension of the problem |
298
|
|
|
self.D = D |
299
|
|
|
# set optimization type |
300
|
|
|
self.optType = optType |
301
|
|
|
# set optimization function |
302
|
|
|
self.benchmark = self.get_benchmark(benchmark) if benchmark is not None else None |
303
|
|
|
if self.benchmark is not None: self.Fun = self.benchmark.function() if self.benchmark is not None else None |
304
|
|
|
# set Lower limits |
305
|
|
|
if Lower is not None: self.Lower = fullArray(Lower, self.D) |
306
|
|
|
elif Lower is None and benchmark is not None: self.Lower = fullArray(self.benchmark.Lower, self.D) |
307
|
|
|
else: self.Lower = fullArray(0, self.D) |
308
|
|
|
# set Upper limits |
309
|
|
|
if Upper is not None: self.Upper = fullArray(Upper, self.D) |
310
|
|
|
elif Upper is None and benchmark is not None: self.Upper = fullArray(self.benchmark.Upper, self.D) |
311
|
|
|
else: self.Upper = fullArray(0, self.D) |
312
|
|
|
# set range |
313
|
|
|
self.bRange = self.Upper - self.Lower |
314
|
|
|
# set repair function |
315
|
|
|
self.frepair = frepair |
316
|
|
|
|
317
|
|
|
def dim(self): |
318
|
|
|
r"""Get the number of dimensions. |
319
|
|
|
|
320
|
|
|
Returns: |
321
|
|
|
int: Dimension of problem optimizing. |
322
|
|
|
""" |
323
|
|
|
return self.D |
324
|
|
|
|
325
|
|
|
def bcLower(self): |
326
|
|
|
r"""Get the array of lower bound constraint. |
327
|
|
|
|
328
|
|
|
Returns: |
329
|
|
|
numpy.ndarray: Lower bound. |
330
|
|
|
""" |
331
|
|
|
return self.Lower |
332
|
|
|
|
333
|
|
|
def bcUpper(self): |
334
|
|
|
r"""Get the array of upper bound constraint. |
335
|
|
|
|
336
|
|
|
Returns: |
337
|
|
|
numpy.ndarray: Upper bound. |
338
|
|
|
""" |
339
|
|
|
return self.Upper |
340
|
|
|
|
341
|
|
|
def bcRange(self): |
342
|
|
|
r"""Get the range of bound constraint. |
343
|
|
|
|
344
|
|
|
Returns: |
345
|
|
|
numpy.ndarray: Range between lower and upper bound. |
346
|
|
|
""" |
347
|
|
|
return self.Upper - self.Lower |
348
|
|
|
|
349
|
|
|
def repair(self, x, rnd=rand): |
350
|
|
|
r"""Repair solution and put the solution in the random position inside of the bounds of problem. |
351
|
|
|
|
352
|
|
|
Arguments: |
353
|
|
|
x (numpy.ndarray): Solution to check and repair if needed. |
354
|
|
|
rnd (mtrand.RandomState): Random number generator. |
355
|
|
|
|
356
|
|
|
Returns: |
357
|
|
|
numpy.ndarray: Fixed solution. |
358
|
|
|
|
359
|
|
|
See Also: |
360
|
|
|
* :func:`NiaPy.util.utility.limitRepair` |
361
|
|
|
* :func:`NiaPy.util.utility.limitInversRepair` |
362
|
|
|
* :func:`NiaPy.util.utility.wangRepair` |
363
|
|
|
* :func:`NiaPy.util.utility.randRepair` |
364
|
|
|
* :func:`NiaPy.util.utility.reflectRepair` |
365
|
|
|
""" |
366
|
|
|
return self.frepair(x, self.Lower, self.Upper, rnd=rnd) |
367
|
|
|
|
368
|
|
|
def nextIter(self): |
369
|
|
|
r"""Increments the number of algorithm iterations.""" |
370
|
|
|
|
371
|
|
|
def start(self): |
372
|
|
|
r"""Start stopwatch.""" |
373
|
|
|
|
374
|
|
|
def eval(self, A): |
375
|
|
|
r"""Evaluate the solution A. |
376
|
|
|
|
377
|
|
|
Arguments: |
378
|
|
|
A (numpy.ndarray): Solution to evaluate. |
379
|
|
|
|
380
|
|
|
Returns: |
381
|
|
|
float: Fitness/function values of solution. |
382
|
|
|
""" |
383
|
|
|
return self.Fun(self.D, A) * self.optType.value |
384
|
|
|
|
385
|
|
|
def isFeasible(self, A): |
386
|
|
|
r"""Check if the solution is feasible. |
387
|
|
|
|
388
|
|
|
Arguments: |
389
|
|
|
A (numpy.ndarray): Solution to check for feasibility. |
390
|
|
|
|
391
|
|
|
Returns: |
392
|
|
|
bool: `True` if solution is in feasible space else `False`. |
393
|
|
|
""" |
394
|
|
|
return False not in (A >= self.Lower) and False not in (A <= self.Upper) |
395
|
|
|
|
396
|
|
|
def stopCond(self): |
397
|
|
|
r"""Check if optimization task should stop. |
398
|
|
|
|
399
|
|
|
Returns: |
400
|
|
|
bool: `True` if stopping condition is meet else `False`. |
401
|
|
|
""" |
402
|
|
|
return False |
403
|
|
|
|
404
|
|
|
class CountingTask(Task): |
405
|
|
|
r"""Optimization task with added counting of function evaluations and algorithm iterations/generations. |
406
|
|
|
|
407
|
|
|
Attributes: |
408
|
|
|
Iters (int): Number of algorithm iterations/generations. |
409
|
|
|
Evals (int): Number of function evaluations. |
410
|
|
|
|
411
|
|
|
See Also: |
412
|
|
|
* :class:`NiaPy.util.Task` |
413
|
|
|
""" |
414
|
|
|
|
415
|
|
|
def __init__(self, **kwargs): |
416
|
|
|
r"""Initialize counting task. |
417
|
|
|
|
418
|
|
|
Args: |
419
|
|
|
**kwargs (Dict[str, Any]): Additional arguments. |
420
|
|
|
|
421
|
|
|
See Also: |
422
|
|
|
* :func:`NiaPy.util.Task.__init__` |
423
|
|
|
""" |
424
|
|
|
Task.__init__(self, **kwargs) |
425
|
|
|
self.Iters, self.Evals = 0, 0 |
426
|
|
|
|
427
|
|
|
def eval(self, A): |
428
|
|
|
r"""Evaluate the solution A. |
429
|
|
|
|
430
|
|
|
This function increments function evaluation counter `self.Evals`. |
431
|
|
|
|
432
|
|
|
Arguments: |
433
|
|
|
A (numpy.ndarray): Solutions to evaluate. |
434
|
|
|
|
435
|
|
|
Returns: |
436
|
|
|
float: Fitness/function values of solution. |
437
|
|
|
|
438
|
|
|
See Also: |
439
|
|
|
* :func:`NiaPy.util.Task.eval` |
440
|
|
|
""" |
441
|
|
|
r = Task.eval(self, A) |
442
|
|
|
self.Evals += 1 |
443
|
|
|
return r |
444
|
|
|
|
445
|
|
|
def evals(self): |
446
|
|
|
r"""Get the number of evaluations made. |
447
|
|
|
|
448
|
|
|
Returns: |
449
|
|
|
int: Number of evaluations made. |
450
|
|
|
""" |
451
|
|
|
return self.Evals |
452
|
|
|
|
453
|
|
|
def iters(self): |
454
|
|
|
r"""Get the number of algorithm iteratins made. |
455
|
|
|
|
456
|
|
|
Returns: |
457
|
|
|
int: Number of generations/iterations made by algorithm. |
458
|
|
|
""" |
459
|
|
|
return self.Iters |
460
|
|
|
|
461
|
|
|
def nextIter(self): |
462
|
|
|
r"""Increases the number of algorithm iterations made. |
463
|
|
|
|
464
|
|
|
This function increments number of algorithm iterations/generations counter `self.Iters`. |
465
|
|
|
""" |
466
|
|
|
self.Iters += 1 |
467
|
|
|
|
468
|
|
|
class StoppingTask(CountingTask): |
469
|
|
|
r"""Optimization task with implemented checking for stopping criterias. |
470
|
|
|
|
471
|
|
|
Attributes: |
472
|
|
|
nGEN (int): Maximum number of algorithm iterations/generations. |
473
|
|
|
nFES (int): Maximum number of function evaluations. |
474
|
|
|
refValue (float): Reference function/fitness values to reach in optimization. |
475
|
|
|
x (numpy.ndarray): Best found individual. |
476
|
|
|
x_f (float): Best found individual function/fitness value. |
477
|
|
|
|
478
|
|
|
See Also: |
479
|
|
|
* :class:`NiaPy.util.CountingTask` |
480
|
|
|
""" |
481
|
|
|
|
482
|
|
|
def __init__(self, nFES=inf, nGEN=inf, refValue=None, **kwargs): |
483
|
|
|
r"""Initialize task class for optimization. |
484
|
|
|
|
485
|
|
|
Arguments: |
486
|
|
|
nFES (Optional[int]): Number of function evaluations. |
487
|
|
|
nGEN (Optional[int]): Number of generations or iterations. |
488
|
|
|
refValue (Optional[float]): Reference value of function/fitness function. |
489
|
|
|
|
490
|
|
|
See Also: |
491
|
|
|
* :func:`NiaPy.util.CountingTask.__init__` |
492
|
|
|
""" |
493
|
|
|
CountingTask.__init__(self, **kwargs) |
494
|
|
|
self.refValue = (-inf if refValue is None else refValue) |
495
|
|
|
self.x, self.x_f = None, inf |
496
|
|
|
self.nFES, self.nGEN = nFES, nGEN |
497
|
|
|
|
498
|
|
|
def eval(self, A): |
499
|
|
|
r"""Evaluate solution. |
500
|
|
|
|
501
|
|
|
Args: |
502
|
|
|
A (numpy.ndarray): Solution to evaluate. |
503
|
|
|
|
504
|
|
|
Returns: |
505
|
|
|
float: Fitness/function value of solution. |
506
|
|
|
|
507
|
|
|
See Also: |
508
|
|
|
* :func:`NiaPy.util.StoppingTask.stopCond` |
509
|
|
|
* :func:`NiaPy.util.CountingTask.eval` |
510
|
|
|
""" |
511
|
|
|
if self.stopCond(): return inf * self.optType.value |
512
|
|
|
x_f = CountingTask.eval(self, A) |
513
|
|
|
if x_f < self.x_f: self.x_f = x_f |
514
|
|
|
return x_f |
515
|
|
|
|
516
|
|
|
def stopCond(self): |
517
|
|
|
r"""Check if stopping condition reached. |
518
|
|
|
|
519
|
|
|
Returns: |
520
|
|
|
bool: `True` if number of function evaluations or number of algorithm iterations/generations or reference values is reach else `False` |
521
|
|
|
""" |
522
|
|
|
return (self.Evals >= self.nFES) or (self.Iters >= self.nGEN) or (self.refValue > self.x_f) |
523
|
|
|
|
524
|
|
|
def stopCondI(self): |
525
|
|
|
r"""Check if stopping condition reached and increase number of iterations. |
526
|
|
|
|
527
|
|
|
Returns: |
528
|
|
|
bool: `True` if number of function evaluations or number of algorithm iterations/generations or reference values is reach else `False`. |
529
|
|
|
|
530
|
|
|
See Also: |
531
|
|
|
* :func:`NiaPy.util.StoppingTask.stopCond` |
532
|
|
|
* :func:`NiaPy.util.CountingTask.nextIter` |
533
|
|
|
""" |
534
|
|
|
r = self.stopCond() |
535
|
|
|
CountingTask.nextIter(self) |
536
|
|
|
return r |
537
|
|
|
|
538
|
|
|
class ThrowingTask(StoppingTask): |
539
|
|
|
r"""Task that throw exceptions when stopping condition is meet. |
540
|
|
|
|
541
|
|
|
See Also: |
542
|
|
|
* :class:`NiaPy.util.StoppingTask` |
543
|
|
|
""" |
544
|
|
|
def __init__(self, **kwargs): |
545
|
|
|
r"""Initialize optimization task. |
546
|
|
|
|
547
|
|
|
Args: |
548
|
|
|
**kwargs (Dict[str, Any]): Additional arguments. |
549
|
|
|
|
550
|
|
|
See Also: |
551
|
|
|
* :func:`NiaPy.util.StoppingTask.__init__` |
552
|
|
|
""" |
553
|
|
|
StoppingTask.__init__(self, **kwargs) |
554
|
|
|
|
555
|
|
|
def stopCondE(self): |
556
|
|
|
r"""Throw exception for the given stopping condition. |
557
|
|
|
|
558
|
|
|
Raises: |
559
|
|
|
* FesException: Thrown when the number of function/fitness evaluations is reached. |
560
|
|
|
* GenException: Thrown when the number of algorithms generations/iterations is reached. |
561
|
|
|
* RefException: Thrown when the reference values is reached. |
562
|
|
|
* TimeException: Thrown when algorithm exceeds time run limit. |
563
|
|
|
""" |
564
|
|
|
# dtime = datetime.now() - self.startTime |
565
|
|
|
if self.Evals >= self.nFES: raise FesException() |
566
|
|
|
if self.Iters >= self.nGEN: raise GenException() |
567
|
|
|
# if self.runTime is not None and self.runTime >= dtime: raise TimeException() |
568
|
|
|
if self.refValue >= self.x_f: raise RefException() |
569
|
|
|
|
570
|
|
|
def eval(self, A): |
571
|
|
|
r"""Evaluate solution. |
572
|
|
|
|
573
|
|
|
Args: |
574
|
|
|
A (numpy.ndarray): Solution to evaluate. |
575
|
|
|
|
576
|
|
|
Returns: |
577
|
|
|
float: Function/fitness values of solution. |
578
|
|
|
|
579
|
|
|
See Also: |
580
|
|
|
* :func:`NiaPy.util.ThrowingTask.stopCondE` |
581
|
|
|
* :func:`NiaPy.util.StoppingTask.eval` |
582
|
|
|
""" |
583
|
|
|
self.stopCondE() |
584
|
|
|
return StoppingTask.eval(self, A) |
585
|
|
|
|
586
|
|
|
class MoveTask(StoppingTask): |
587
|
|
|
def __init__(self, o=None, fo=None, M=None, fM=None, optF=None, **kwargs): |
588
|
|
|
r"""Initialize task class for optimization. |
589
|
|
|
|
590
|
|
|
Arguments: |
591
|
|
|
o (numpy.ndarray[Union[float, int]]): Array for shifting. |
592
|
|
|
of (Callable[numpy.ndarray[Union[float, int]]]): Function applied on shifted input. |
593
|
|
|
M (numpy.ndarray[Union[float, int]]): Matrix for rotating. |
594
|
|
|
fM (Callable[numpy.ndarray[Union[float, int]]]): Function applied after rotating. |
595
|
|
|
|
596
|
|
|
See Also: |
597
|
|
|
* :func:`NiaPy.util.StoppingTask.__init__` |
598
|
|
|
""" |
599
|
|
|
StoppingTask.__init__(self, **kwargs) |
600
|
|
|
self.o = o if isinstance(o, ndarray) or o is None else asarray(o) |
601
|
|
|
self.M = M if isinstance(M, ndarray) or M is None else asarray(M) |
602
|
|
|
self.fo, self.fM, self.optF = fo, fM, optF |
603
|
|
|
|
604
|
|
|
def eval(self, A): |
605
|
|
|
r"""Evaluate the solution. |
606
|
|
|
|
607
|
|
|
Args: |
608
|
|
|
A (numpy.ndarray): Solution to evaluate |
609
|
|
|
|
610
|
|
|
Returns: |
611
|
|
|
float: Fitness/function value of solution. |
612
|
|
|
|
613
|
|
|
See Also: |
614
|
|
|
* :func:`NiaPy.util.StoppingTask.stopCond` |
615
|
|
|
* :func:`NiaPy.util.StoppingTask.eval` |
616
|
|
|
""" |
617
|
|
|
if self.stopCond(): return inf * self.optType.value |
618
|
|
|
X = A - self.o if self.o is not None else A |
619
|
|
|
X = self.fo(X) if self.fo is not None else X |
620
|
|
|
X = dot(X, self.M) if self.M is not None else X |
621
|
|
|
X = self.fM(X) if self.fM is not None else X |
622
|
|
|
r = StoppingTask.eval(self, X) + (self.optF if self.optF is not None else 0) |
623
|
|
|
if r <= self.x_f: self.x, self.x_f = A, r |
624
|
|
|
return r |
625
|
|
|
|
626
|
|
|
class ScaledTask(Task): |
627
|
|
|
r"""Scaled task. |
628
|
|
|
|
629
|
|
|
Attributes: |
630
|
|
|
_task (Task): Optimization task with evaluation function. |
631
|
|
|
Lower (numpy.ndarray): Scaled lower limit of search space. |
632
|
|
|
Upper (numpy.ndarray): Scaled upper limit of search space. |
633
|
|
|
|
634
|
|
|
See Also: |
635
|
|
|
* :class:`NiaPy.util.Task` |
636
|
|
|
""" |
637
|
|
|
def __init__(self, task, Lower, Upper, **kwargs): |
638
|
|
|
r"""Initialize scaled task. |
639
|
|
|
|
640
|
|
|
Args: |
641
|
|
|
task (Task): Optimization task to scale to new bounds. |
642
|
|
|
Lower (Union[float, int, numpy.ndarray]): New lower bounds. |
643
|
|
|
Upper (Union[float, int, numpy.ndarray]): New upper bounds. |
644
|
|
|
**kwargs (Dict[str, Any]): Additional arguments. |
645
|
|
|
|
646
|
|
|
See Also: |
647
|
|
|
* :func:`NiaPy.util.fullArray` |
648
|
|
|
""" |
649
|
|
|
Task.__init__(self) |
650
|
|
|
self._task = task |
651
|
|
|
self.D = self._task.D |
652
|
|
|
self.Lower, self.Upper = fullArray(Lower, self.D), fullArray(Upper, self.D) |
653
|
|
|
self.bRange = fabs(Upper - Lower) |
654
|
|
|
|
655
|
|
|
def stopCond(self): |
656
|
|
|
r"""Test for stopping condition. |
657
|
|
|
|
658
|
|
|
This function uses `self._task` for checking the stopping criteria. |
659
|
|
|
|
660
|
|
|
Returns: |
661
|
|
|
bool: `True` if stopping condition is meet else `False`. |
662
|
|
|
""" |
663
|
|
|
return self._task.stopCond() |
664
|
|
|
|
665
|
|
|
def stopCondI(self): |
666
|
|
|
r"""Test for stopping condition and increments the number of algorithm generations/iterations. |
667
|
|
|
|
668
|
|
|
This function uses `self._task` for checking the stopping criteria. |
669
|
|
|
|
670
|
|
|
Returns: |
671
|
|
|
bool: `True` if stopping condition is meet else `False`. |
672
|
|
|
""" |
673
|
|
|
return self._task.stopCondI() |
674
|
|
|
|
675
|
|
|
def eval(self, A): |
676
|
|
|
r"""Evaluate solution. |
677
|
|
|
|
678
|
|
|
Args: |
679
|
|
|
A (numpy.ndarray): Solution for calculating function/fitness value. |
680
|
|
|
|
681
|
|
|
Returns: |
682
|
|
|
float: Function values of solution. |
683
|
|
|
""" |
684
|
|
|
return self._task.eval(A) |
685
|
|
|
|
686
|
|
|
def evals(self): |
687
|
|
|
r"""Get the number of function evaluations. |
688
|
|
|
|
689
|
|
|
Returns: |
690
|
|
|
int: Number of function evaluations. |
691
|
|
|
""" |
692
|
|
|
return self._task.evals() |
693
|
|
|
|
694
|
|
|
def iters(self): |
695
|
|
|
r"""Get the number of algorithms generations/iterations. |
696
|
|
|
|
697
|
|
|
Returns: |
698
|
|
|
int: Number of algorithms generations/iterations. |
699
|
|
|
""" |
700
|
|
|
return self._task.iters() |
701
|
|
|
|
702
|
|
|
def nextIter(self): |
703
|
|
|
r"""Increment the number of iterations/generations. |
704
|
|
|
|
705
|
|
|
Function uses `self._task` to increment number of generations/iterations. |
706
|
|
|
""" |
707
|
|
|
self._task.nextIter() |
708
|
|
|
|
709
|
|
|
class TaskConvPrint(StoppingTask): |
710
|
|
|
r"""Task class with printing out new global best solutions found. |
711
|
|
|
|
712
|
|
|
Attributes: |
713
|
|
|
xb (numpy.ndarray): Global best solution. |
714
|
|
|
xb_f (float): Global best function/fitness values. |
715
|
|
|
|
716
|
|
|
See Also: |
717
|
|
|
* :class:`NiaPy.util.StoppingTask` |
718
|
|
|
""" |
719
|
|
|
def __init__(self, **kwargs): |
720
|
|
|
r"""Initialize TaskConvPrint class. |
721
|
|
|
|
722
|
|
|
Args: |
723
|
|
|
**kwargs (Dict[str, Any]): Additional arguments. |
724
|
|
|
|
725
|
|
|
See Also: |
726
|
|
|
* :func:`NiaPy.util.StoppingTask.__init__` |
727
|
|
|
""" |
728
|
|
|
StoppingTask.__init__(self, **kwargs) |
729
|
|
|
self.xb, self.xb_f = None, inf |
730
|
|
|
|
731
|
|
|
def eval(self, A): |
732
|
|
|
r"""Evaluate solution. |
733
|
|
|
|
734
|
|
|
Args: |
735
|
|
|
A (nupy.ndarray): Solution to evaluate. |
736
|
|
|
|
737
|
|
|
Returns: |
738
|
|
|
float: Function/Fitness values of solution. |
739
|
|
|
|
740
|
|
|
See Also: |
741
|
|
|
* :func:`NiaPy.util.StoppingTask.eval` |
742
|
|
|
""" |
743
|
|
|
x_f = StoppingTask.eval(self, A) |
744
|
|
|
if self.x_f != self.xb_f: |
745
|
|
|
self.xb, self.xb_f = A, x_f |
746
|
|
|
logger.info('nFES:%d nGEN:%d => %s -> %s' % (self.Evals, self.Iters, self.xb, self.xb_f * self.optType.value)) |
747
|
|
|
return x_f |
748
|
|
|
|
749
|
|
|
class TaskConvSave(StoppingTask): |
750
|
|
|
r"""Task class with logging of function evaluations need to reach some function vale. |
751
|
|
|
|
752
|
|
|
Attributes: |
753
|
|
|
evals (List[int]): List of ints representing when the new global best was found. |
754
|
|
|
x_f_vals (List[float]): List of floats representing function/fitness values found. |
755
|
|
|
|
756
|
|
|
See Also: |
757
|
|
|
* :class:`NiaPy.util.StoppingTask` |
758
|
|
|
""" |
759
|
|
|
def __init__(self, **kwargs): |
760
|
|
|
r"""Initialize TaskConvSave class. |
761
|
|
|
|
762
|
|
|
Args: |
763
|
|
|
**kwargs (Dict[str, Any]): Additional arguments. |
764
|
|
|
|
765
|
|
|
See Also: |
766
|
|
|
* :func:`NiaPy.util.StoppingTask.__init__` |
767
|
|
|
""" |
768
|
|
|
StoppingTask.__init__(self, **kwargs) |
769
|
|
|
self.evals = [] |
770
|
|
|
self.x_f_vals = [] |
771
|
|
|
|
772
|
|
|
def eval(self, A): |
773
|
|
|
r"""Evaluate solution. |
774
|
|
|
|
775
|
|
|
Args: |
776
|
|
|
A (numpy.ndarray): Individual/solution to evaluate. |
777
|
|
|
|
778
|
|
|
Returns: |
779
|
|
|
float: Function/fitness values of individual. |
780
|
|
|
|
781
|
|
|
See Also: |
782
|
|
|
* :func:`SNiaPy.util.toppingTask.eval` |
783
|
|
|
""" |
784
|
|
|
x_f = StoppingTask.eval(self, A) |
785
|
|
|
if x_f <= self.x_f: |
786
|
|
|
self.evals.append(self.Evals) |
787
|
|
|
self.x_f_vals.append(x_f) |
788
|
|
|
return x_f |
789
|
|
|
|
790
|
|
|
def return_conv(self): |
791
|
|
|
r"""Get values of x and y axis for plotting covariance graph. |
792
|
|
|
|
793
|
|
|
Returns: |
794
|
|
|
Tuple[List[int], List[float]]: |
795
|
|
|
1. List of ints of function evaluations. |
796
|
|
|
2. List of ints of function/fitness values. |
797
|
|
|
""" |
798
|
|
|
return self.evals, self.x_f_vals |
799
|
|
|
|
800
|
|
|
class TaskConvPlot(StoppingTask): |
801
|
|
|
r"""Task class with ability of showing convergence graph. |
802
|
|
|
|
803
|
|
|
Attributes: |
804
|
|
|
iters (List[int]): List of ints representing when the new global best was found. |
805
|
|
|
x_fs (List[float]): List of floats representing function/fitness values found. |
806
|
|
|
|
807
|
|
|
See Also: |
808
|
|
|
* :class:`NiaPy.util.StoppingTask` |
809
|
|
|
""" |
810
|
|
|
def __init__(self, **kwargs): |
811
|
|
|
r"""TODO. |
812
|
|
|
|
813
|
|
|
Args: |
814
|
|
|
**kwargs (Dict[str, Any]): Additional arguments. |
815
|
|
|
|
816
|
|
|
See Also: |
817
|
|
|
* :func:`NiaPy.util.StoppingTask.__init__` |
818
|
|
|
""" |
819
|
|
|
StoppingTask.__init__(self, **kwargs) |
820
|
|
|
self.x_fs, self.iters = [], [] |
821
|
|
|
self.fig = plt.figure() |
822
|
|
|
self.ax = self.fig.subplots(nrows=1, ncols=1) |
823
|
|
|
self.ax.set_xlim(0, self.nFES) |
824
|
|
|
self.line, = self.ax.plot(self.iters, self.x_fs, animated=True) |
825
|
|
|
self.ani = anim.FuncAnimation(self.fig, self.updatePlot, blit=True) |
826
|
|
|
self.showPlot() |
827
|
|
|
|
828
|
|
|
def eval(self, A): |
829
|
|
|
r"""Evaluate solution. |
830
|
|
|
|
831
|
|
|
Args: |
832
|
|
|
A (numpy.ndarray): Solution to evaluate. |
833
|
|
|
|
834
|
|
|
Returns: |
835
|
|
|
float: Fitness/function values of solution. |
836
|
|
|
""" |
837
|
|
|
x_f = StoppingTask.eval(self, A) |
838
|
|
|
if not self.x_fs: self.x_fs.append(x_f) |
839
|
|
|
elif x_f < self.x_fs[-1]: self.x_fs.append(x_f) |
840
|
|
|
else: self.x_fs.append(self.x_fs[-1]) |
841
|
|
|
self.iters.append(self.Evals) |
842
|
|
|
return x_f |
843
|
|
|
|
844
|
|
|
def showPlot(self): |
845
|
|
|
r"""Animation updating function.""" |
846
|
|
|
plt.show(block=False) |
847
|
|
|
plt.pause(0.001) |
848
|
|
|
|
849
|
|
|
def updatePlot(self, frame): |
850
|
|
|
r"""Update mathplotlib figure. |
851
|
|
|
|
852
|
|
|
Args: |
853
|
|
|
frame (): TODO |
854
|
|
|
|
855
|
|
|
Returns: |
856
|
|
|
Tuple[List[float], Any]: |
857
|
|
|
1. Line |
858
|
|
|
""" |
859
|
|
|
if self.x_fs: |
860
|
|
|
max_fs, min_fs = self.x_fs[0], self.x_fs[-1] |
861
|
|
|
self.ax.set_ylim(min_fs + 1, max_fs + 1) |
862
|
|
|
self.line.set_data(self.iters, self.x_fs) |
863
|
|
|
return self.line, |
864
|
|
|
|
865
|
|
|
class TaskComposition(MoveTask): |
866
|
|
|
def __init__(self, benchmarks=None, rho=None, lamb=None, bias=None, **kwargs): |
867
|
|
|
r"""Initialize of composite function problem. |
868
|
|
|
|
869
|
|
|
Arguments: |
870
|
|
|
benchmarks (List[Benchmark]): Optimization function to use in composition |
871
|
|
|
delta (numpy.ndarray[float]): TODO |
872
|
|
|
lamb (numpy.ndarray[float]): TODO |
873
|
|
|
bias (numpy.ndarray[float]): TODO |
874
|
|
|
|
875
|
|
|
See Also: |
876
|
|
|
* :func:`NiaPy.util.MoveTask.__init__` |
877
|
|
|
|
878
|
|
|
TODO: |
879
|
|
|
Class is a work in progress. |
880
|
|
|
""" |
881
|
|
|
MoveTask.__init__(self, **kwargs) |
882
|
|
|
|
883
|
|
|
def eval(self, A): |
884
|
|
|
r"""TODO. |
885
|
|
|
|
886
|
|
|
Args: |
887
|
|
|
A: |
888
|
|
|
|
889
|
|
|
Returns: |
890
|
|
|
float: |
891
|
|
|
|
892
|
|
|
Todo: |
893
|
|
|
Usage of multiple functions on the same time |
894
|
|
|
""" |
895
|
|
|
return inf |
896
|
|
|
|
897
|
|
|
# vim: tabstop=3 noexpandtab shiftwidth=3 softtabstop=3 |
898
|
|
|
|