Completed
Push — master ( e51968...a7558a )
by
unknown
13s queued 11s
created

NiaPy.task.task.StoppingTask.plot()   A

Complexity

Conditions 1

Size

Total Lines 7
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 6
nop 1
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
# encoding=utf8
2
3
"""The implementation of tasks."""
4
5
import logging
6
from enum import Enum
7
8
from matplotlib import pyplot as plt
9
from numpy import inf, random as rand
10
11
from NiaPy.util import (
12
    limit_repair,
13
    fullArray,
14
    FesException,
15
    GenException,
16
    RefException
17
)
18
from NiaPy.benchmarks.utility import Utility
19
20
21
logging.basicConfig()
22
logger = logging.getLogger("NiaPy.task.Task")
23
logger.setLevel("INFO")
24
25
26
class OptimizationType(Enum):
27
    r"""Enum representing type of optimization.
28
29
    Attributes:
30
            MINIMIZATION (int): Represents minimization problems and is default optimization type of all algorithms.
31
            MAXIMIZATION (int): Represents maximization problems.
32
33
    """
34
35
    MINIMIZATION = 1.0
36
    MAXIMIZATION = -1.0
37
38
39
class Task:
40
    r"""Class representing problem to solve with optimization.
41
42
    Date:
43
            2019
44
45
    Author:
46
            Klemen Berkovič and others
47
48
    Attributes:
49
            D (int): Dimension of the problem.
50
            Lower (numpy.ndarray): Lower bounds of the problem.
51
            Upper (numpy.ndarray): Upper bounds of the problem.
52
            bRange (numpy.ndarray): Search range between upper and lower limits.
53
            optType (OptimizationType): Optimization type to use.
54
55
    See Also:
56
            * :class:`NiaPy.util.Utility`
57
58
    """
59
60
    D = 0
61
    benchmark = None
62
    Lower, Upper, bRange = inf, inf, inf
63
    optType = OptimizationType.MINIMIZATION
64
65
    def __init__(self, D=0, optType=OptimizationType.MINIMIZATION, benchmark=None, Lower=None, Upper=None, frepair=limit_repair, **kwargs):
66
        r"""Initialize task class for optimization.
67
68
        Arguments:
69
                D (Optional[int]): Number of dimensions.
70
                optType (Optional[OptimizationType]): Set the type of optimization.
71
                benchmark (Union[str, Benchmark]): Problem to solve with optimization.
72
                Lower (Optional[numpy.ndarray]): Lower limits of the problem.
73
                Upper (Optional[numpy.ndarray]): Upper limits of the problem.
74
                frepair (Optional[Callable[[numpy.ndarray, numpy.ndarray, numpy.ndarray, Dict[str, Any]], numpy.ndarray]]): Function for reparing individuals components to desired limits.
75
76
        See Also:
77
                * `func`:NiaPy.util.Utility.__init__`
78
                * `func`:NiaPy.util.Utility.repair`
79
80
        """
81
82
        # dimension of the problem
83
        self.D = D
84
        # set optimization type
85
        self.optType = optType
86
        # set optimization function
87
        self.benchmark = Utility().get_benchmark(benchmark) if benchmark is not None else None
88
89
        if self.benchmark is not None:
90
            self.Fun = self.benchmark.function() if self.benchmark is not None else None
91
92
        # set Lower limits
93
        if Lower is not None:
94
            self.Lower = fullArray(Lower, self.D)
95
        elif Lower is None and benchmark is not None:
96
            self.Lower = fullArray(self.benchmark.Lower, self.D)
97
        else:
98
            self.Lower = fullArray(0, self.D)
99
100
        # set Upper limits
101
        if Upper is not None:
102
            self.Upper = fullArray(Upper, self.D)
103
        elif Upper is None and benchmark is not None:
104
            self.Upper = fullArray(self.benchmark.Upper, self.D)
105
        else:
106
            self.Upper = fullArray(0, self.D)
107
108
        # set range
109
        self.bRange = self.Upper - self.Lower
110
        # set repair function
111
        self.frepair = frepair
112
113
    def dim(self):
114
        r"""Get the number of dimensions.
115
116
        Returns:
117
                int: Dimension of problem optimizing.
118
119
        """
120
121
        return self.D
122
123
    def bcLower(self):
124
        r"""Get the array of lower bound constraint.
125
126
        Returns:
127
                numpy.ndarray: Lower bound.
128
129
        """
130
131
        return self.Lower
132
133
    def bcUpper(self):
134
        r"""Get the array of upper bound constraint.
135
136
        Returns:
137
                numpy.ndarray: Upper bound.
138
139
        """
140
141
        return self.Upper
142
143
    def bcRange(self):
144
        r"""Get the range of bound constraint.
145
146
        Returns:
147
                numpy.ndarray: Range between lower and upper bound.
148
149
        """
150
151
        return self.Upper - self.Lower
152
153
    def repair(self, x, rnd=rand):
154
        r"""Repair solution and put the solution in the random position inside of the bounds of problem.
155
156
        Arguments:
157
                x (numpy.ndarray): Solution to check and repair if needed.
158
                rnd (mtrand.RandomState): Random number generator.
159
160
        Returns:
161
                numpy.ndarray: Fixed solution.
162
163
        See Also:
164
                * :func:`NiaPy.util.limitRepair`
165
                * :func:`NiaPy.util.limitInversRepair`
166
                * :func:`NiaPy.util.wangRepair`
167
                * :func:`NiaPy.util.randRepair`
168
                * :func:`NiaPy.util.reflectRepair`
169
170
        """
171
172
        return self.frepair(x, self.Lower, self.Upper, rnd=rnd)
173
174
    def nextIter(self):
175
        r"""Increments the number of algorithm iterations."""
176
177
    def start(self):
178
        r"""Start stopwatch."""
179
180
    def eval(self, A):
181
        r"""Evaluate the solution A.
182
183
        Arguments:
184
                A (numpy.ndarray): Solution to evaluate.
185
186
        Returns:
187
                float: Fitness/function values of solution.
188
189
        """
190
191
        return self.Fun(self.D, A) * self.optType.value
192
193
    def isFeasible(self, A):
194
        r"""Check if the solution is feasible.
195
196
        Arguments:
197
                A (Union[numpy.ndarray, Individual]): Solution to check for feasibility.
198
199
        Returns:
200
                bool: `True` if solution is in feasible space else `False`.
201
202
        """
203
204
        return False not in (A >= self.Lower) and False not in (A <= self.Upper)
205
206
    def stopCond(self):
207
        r"""Check if optimization task should stop.
208
209
        Returns:
210
                bool: `True` if stopping condition is meet else `False`.
211
212
        """
213
214
        return False
215
216
217
class CountingTask(Task):
218
    r"""Optimization task with added counting of function evaluations and algorithm iterations/generations.
219
220
    Attributes:
221
            Iters (int): Number of algorithm iterations/generations.
222
            Evals (int): Number of function evaluations.
223
224
    See Also:
225
            * :class:`NiaPy.util.Task`
226
227
    """
228
229
    def __init__(self, **kwargs):
230
        r"""Initialize counting task.
231
232
        Args:
233
                **kwargs (Dict[str, Any]): Additional arguments.
234
235
        See Also:
236
                * :func:`NiaPy.util.Task.__init__`
237
238
        """
239
240
        Task.__init__(self, **kwargs)
241
        self.Iters, self.Evals = 0, 0
242
243
    def eval(self, A):
244
        r"""Evaluate the solution A.
245
246
        This function increments function evaluation counter `self.Evals`.
247
248
        Arguments:
249
                A (numpy.ndarray): Solutions to evaluate.
250
251
        Returns:
252
                float: Fitness/function values of solution.
253
254
        See Also:
255
                * :func:`NiaPy.util.Task.eval`
256
257
        """
258
259
        r = Task.eval(self, A)
260
        self.Evals += 1
261
        return r
262
263
    def evals(self):
264
        r"""Get the number of evaluations made.
265
266
        Returns:
267
                int: Number of evaluations made.
268
269
        """
270
271
        return self.Evals
272
273
    def iters(self):
274
        r"""Get the number of algorithm iteratins made.
275
276
        Returns:
277
                int: Number of generations/iterations made by algorithm.
278
279
        """
280
281
        return self.Iters
282
283
    def nextIter(self):
284
        r"""Increases the number of algorithm iterations made.
285
286
        This function increments number of algorithm iterations/generations counter `self.Iters`.
287
288
        """
289
290
        self.Iters += 1
291
292
293
class StoppingTask(CountingTask):
294
    r"""Optimization task with implemented checking for stopping criterias.
295
296
    Attributes:
297
            nGEN (int): Maximum number of algorithm iterations/generations.
298
            nFES (int): Maximum number of function evaluations.
299
            refValue (float): Reference function/fitness values to reach in optimization.
300
            x (numpy.ndarray): Best found individual.
301
            x_f (float): Best found individual function/fitness value.
302
303
    See Also:
304
            * :class:`NiaPy.util.CountingTask`
305
306
    """
307
308
    def __init__(self, nFES=inf, nGEN=inf, refValue=None, logger=False, **kwargs):
309
        r"""Initialize task class for optimization.
310
311
        Arguments:
312
                nFES (Optional[int]): Number of function evaluations.
313
                nGEN (Optional[int]): Number of generations or iterations.
314
                refValue (Optional[float]): Reference value of function/fitness function.
315
                logger (Optional[bool]): Enable/disable logging of improvements.
316
317
        Note:
318
                Storing improvements during the evolutionary cycle is
319
                captured in self.n_evals and self.x_f_vals
320
321
        See Also:
322
                * :func:`NiaPy.util.CountingTask.__init__`
323
324
        """
325
326
        CountingTask.__init__(self, **kwargs)
327
        self.refValue = (-inf if refValue is None else refValue)
328
        self.logger = logger
329
        self.x, self.x_f = None, inf
330
        self.nFES, self.nGEN = nFES, nGEN
331
        self.n_evals = []
332
        self.x_f_vals = []
333
334
    def eval(self, A):
335
        r"""Evaluate solution.
336
337
        Args:
338
                A (numpy.ndarray): Solution to evaluate.
339
340
        Returns:
341
                float: Fitness/function value of solution.
342
343
        See Also:
344
                * :func:`NiaPy.util.StoppingTask.stopCond`
345
                * :func:`NiaPy.util.CountingTask.eval`
346
347
        """
348
349
        if self.stopCond():
350
            return inf * self.optType.value
351
352
        x_f = CountingTask.eval(self, A)
353
354
        if x_f < self.x_f:
355
            self.x_f = x_f
356
            self.n_evals.append(self.Evals)
357
            self.x_f_vals.append(x_f)
358
            if self.logger:
359
                logger.info('nFES:%d => %s' % (self.Evals, self.x_f))
360
361
        return x_f
362
363
    def stopCond(self):
364
        r"""Check if stopping condition reached.
365
366
        Returns:
367
                bool: `True` if number of function evaluations or number of algorithm iterations/generations or reference values is reach else `False`
368
369
        """
370
371
        return (self.Evals >= self.nFES) or (self.Iters >= self.nGEN) or (self.refValue > self.x_f)
372
373
    def stopCondI(self):
374
        r"""Check if stopping condition reached and increase number of iterations.
375
376
        Returns:
377
                bool: `True` if number of function evaluations or number of algorithm iterations/generations or reference values is reach else `False`.
378
379
        See Also:
380
                * :func:`NiaPy.util.StoppingTask.stopCond`
381
                * :func:`NiaPy.util.CountingTask.nextIter`
382
383
        """
384
385
        r = self.stopCond()
386
        CountingTask.nextIter(self)
387
        return r
388
389
    def return_conv(self):
390
        r"""Get values of x and y axis for plotting covariance graph.
391
392
        Returns:
393
                Tuple[List[int], List[float]]:
394
                    1. List of ints of function evaluations.
395
                    2. List of ints of function/fitness values.
396
397
        """
398
        return self.n_evals, self.x_f_vals
399
400
    def plot(self):
401
        """Plot a simple convergence graph."""
402
        plt.plot(self.n_evals, self.x_f_vals)
403
        plt.xlabel('nFes')
404
        plt.ylabel('Fitness')
405
        plt.title('Convergence graph')
406
        plt.show()
407
408
409
class ThrowingTask(StoppingTask):
410
    r"""Task that throw exceptions when stopping condition is meet.
411
412
    See Also:
413
            * :class:`NiaPy.util.StoppingTask`
414
415
    """
416
417
    def __init__(self, **kwargs):
418
        r"""Initialize optimization task.
419
420
        Args:
421
                **kwargs (Dict[str, Any]): Additional arguments.
422
423
        See Also:
424
                * :func:`NiaPy.util.StoppingTask.__init__`
425
426
        """
427
428
        StoppingTask.__init__(self, **kwargs)
429
430
    def stopCondE(self):
431
        r"""Throw exception for the given stopping condition.
432
433
        Raises:
434
                * FesException: Thrown when the number of function/fitness evaluations is reached.
435
                * GenException: Thrown when the number of algorithms generations/iterations is reached.
436
                * RefException: Thrown when the reference values is reached.
437
                * TimeException: Thrown when algorithm exceeds time run limit.
438
439
        """
440
441
        # dtime = datetime.now() - self.startTime
442
        if self.Evals >= self.nFES:
443
            raise FesException()
444
        if self.Iters >= self.nGEN:
445
            raise GenException()
446
        # if self.runTime is not None and self.runTime >= dtime: raise TimeException()
447
        if self.refValue >= self.x_f:
448
            raise RefException()
449
450
    def eval(self, A):
451
        r"""Evaluate solution.
452
453
        Args:
454
                A (numpy.ndarray): Solution to evaluate.
455
456
        Returns:
457
                float: Function/fitness values of solution.
458
459
        See Also:
460
                * :func:`NiaPy.util.ThrowingTask.stopCondE`
461
                * :func:`NiaPy.util.StoppingTask.eval`
462
463
        """
464
465
        self.stopCondE()
466
        return StoppingTask.eval(self, A)
467