Passed
Pull Request — master (#205)
by
unknown
01:02
created

ForestOptimizationAlgorithm.removeLifeTimeExceeded()   A

Complexity

Conditions 1

Size

Total Lines 19
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 6
nop 4
dl 0
loc 19
rs 10
c 0
b 0
f 0
1
# encoding=utf8
2
# pylint: disable=mixed-indentation, trailing-whitespace, multiple-statements, attribute-defined-outside-init, logging-not-lazy, redefined-builtin, line-too-long, no-self-use, arguments-differ, no-else-return, bad-continuation
3
import logging
4
from numpy import where, apply_along_axis, zeros, append, ndarray, delete, arange, argmin, absolute, int32
5
from NiaPy.algorithms.algorithm import Algorithm
6
from NiaPy.util import limitRepair
7
8
__all__ = ['ForestOptimizationAlgorithm']
9
10
logging.basicConfig()
11
logger = logging.getLogger('NiaPy.algorithms.basic')
12
logger.setLevel('INFO')
13
14
class ForestOptimizationAlgorithm(Algorithm):
15
    r"""Implementation of Forest Optimization Algorithm.
16
17
    Algorithm:
18
        Forest Optimization Algorithm
19
20
    Date:
21
        2019
22
23
    Authors:
24
        Luka Pečnik
25
26
    License:
27
        MIT
28
29
    Reference paper:
30
        Manizheh Ghaemi, Mohammad-Reza Feizi-Derakhshi, Forest Optimization Algorithm, Expert Systems with Applications, Volume 41, Issue 15, 2014, Pages 6676-6687, ISSN 0957-4174, https://doi.org/10.1016/j.eswa.2014.05.009.
31
32
    References URL:
33
        Implementation is based on the following MATLAB code: https://github.com/cominsys/FOA
34
35
    Attributes:
36
        Name (List[str]): List of strings representing algorithm name.
37
        lt (int): Life time of trees parameter.
38
        al (int): Area limit parameter.
39
        lsc (int): Local seeding changes parameter.
40
        gsc (int): Global seeding changes parameter.
41
        tr (float): Transfer rate parameter.
42
43
    See Also:
44
        * :class:`NiaPy.algorithms.Algorithm`
45
    """
46
    Name = ['ForestOptimizationAlgorithm', 'FOA']
47
48
    @staticmethod
49
    def algorithmInfo():
50
        return r"""
51
        Description: Forest Optimization Algorithm is inspired by few trees in the forests which can survive for several decades, while other trees could live for a limited period.
52
        Authors: Manizheh Ghaemi, Mohammad-Reza Feizi-Derakhshi
53
        Year: 2014
54
    Main reference: Manizheh Ghaemi, Mohammad-Reza Feizi-Derakhshi, Forest Optimization Algorithm, Expert Systems with Applications, Volume 41, Issue 15, 2014, Pages 6676-6687, ISSN 0957-4174, https://doi.org/10.1016/j.eswa.2014.05.009.
55
    """
56
57 View Code Duplication
    @staticmethod
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
58
    def typeParameters():
59
        r"""Get dictionary with functions for checking values of parameters.
60
61
        Returns:
62
            Dict[str, Callable]:
63
                * lt (Callable[[int], bool]): Checks if life time parameter has a proper value.
64
                * al (Callable[[int], bool]): Checks if area limit parameter has a proper value.
65
                * lsc (Callable[[int], bool]): Checks if local seeding changes parameter has a proper value.
66
                * gsc (Callable[[int], bool]): Checks if global seeding changes parameter has a proper value.
67
                * tr (Callable[[float], bool]): Checks if transfer rate parameter has a proper value.
68
69
        See Also:
70
            * :func:`NiaPy.algorithms.algorithm.Algorithm.typeParameters`
71
        """
72
        d = Algorithm.typeParameters()
73
        d.update({
74
            'lt': lambda x: isinstance(x, int) and x > 0,
75
            'al': lambda x: isinstance(x, int) and x > 0,
76
            'lsc': lambda x: isinstance(x, int) and x > 0,
77
            'gsc': lambda x: isinstance(x, int) and x > 0,
78
            'tr': lambda x: isinstance(x, float) and 0 <= x <= 1,
79
        })
80
        return d
81
82
    def setParameters(self, NP=10, lt=3, al=10, lsc=1, gsc=1, tr=0.3, **ukwargs):
83
        r"""Set the parameters of the algorithm.
84
85
        Args:
86
            NP (Optional[int]): Population size.
87
            lt (Optional[int]): Life time parameter.
88
            al (Optional[int]): Area limit parameter.
89
            lsc (Optional[int]): Local seeding changes parameter.
90
            gsc (Optional[int]): Global seeding changes parameter.
91
            tr (Optional[float]): Transfer rate parameter.
92
            ukwargs (Dict[str, Any]): Additional arguments.
93
94
        See Also:
95
            * :func:`NiaPy.algorithms.Algorithm.setParameters`
96
        """
97
        Algorithm.setParameters(self, NP=NP)
98
        self.lt, self.al, self.lsc, self.gsc, self.tr = lt, al, lsc, gsc, tr
99
        if ukwargs: logger.info('Unused arguments: %s' % (ukwargs))
100
101
    def localSeeding(self, task, trees):
102
        r"""Local optimum search stage.
103
104
        Args:
105
            task (Task): Optimization task.
106
            trees (numpy.ndarray): Zero age trees for local seeding.
107
108
        Returns:
109
            numpy.ndarray: Resulting zero age trees.
110
        """
111
        n = trees.shape[0]
112
        deltas = self.uniform(-self.dx, self.dx, (n, self.lsc))
113
        deltas = append(deltas, zeros((n, task.D - self.lsc)), axis=1)
114
        perms = self.rand([deltas.shape[0], deltas.shape[1]]).argsort(1)
115
        deltas = deltas[arange(deltas.shape[0])[:, None], perms]
116
        trees += deltas
117
        trees = apply_along_axis(limitRepair, 1, trees, task.Lower, task.Upper)
118
        return trees
119
120
    def globalSeeding(self, task, candidates, size):
121
        r"""Global optimum search stage that should prevent getting stuck in a local optimum.
122
123
        Args:
124
            task (Task): Optimization task.
125
            candidates (numpy.ndarray): Candidate population for global seeding.
126
            size (int): Number of trees to produce.
127
128
        Returns:
129
            numpy.ndarray: Resulting trees.
130
        """
131
        seeds = candidates[self.randint(len(candidates), D=size)]
132
        deltas = self.uniform(task.benchmark.Lower, task.benchmark.Upper, (size, self.gsc))
133
        deltas = append(deltas, zeros((size, task.D - self.gsc)), axis=1)
134
        perms = self.rand([deltas.shape[0], deltas.shape[1]]).argsort(1)
135
        deltas = deltas[arange(deltas.shape[0])[:, None], perms]
136
137
        deltas = deltas.flatten()
138
        seeds = seeds.flatten()
139
        seeds[deltas != 0] = deltas[deltas != 0]
140
141
        return seeds.reshape(size, task.D)
142
143
    def removeLifeTimeExceeded(self, trees, candidates, age):
144
        r"""Remove dead trees.
145
146
        Args:
147
            trees (numpy.ndarray): Population to test.
148
            candidates (numpy.ndarray): Candidate population array to be updated.
149
            age (numpy.ndarray[int32]): Age of trees.
150
151
        Returns:
152
            Tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray[int32]]:
153
                1. Alive trees.
154
                2. New candidate population.
155
                3. Age of trees.
156
        """
157
        lifeTimeExceeded = where(age > self.lt)
158
        candidates = trees[lifeTimeExceeded]
159
        trees = delete(trees, lifeTimeExceeded, axis=0)
160
        age = delete(age, lifeTimeExceeded, axis=0)
161
        return trees, candidates, age
162
163
    def survivalOfTheFittest(self, task, trees, candidates, age):
164
        r"""Evaluate and filter current population.
165
166
        Args:
167
            task (Task): Optimization task.
168
            trees (numpy.ndarray): Population to evaluate.
169
            candidates (numpy.ndarray): Candidate population array to be updated.
170
            age (numpy.ndarray[int32]): Age of trees.
171
172
        Returns:
173
            Tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray[float], numpy.ndarray[int32]]:
174
                1. Trees sorted by fitness value.
175
                2. Updated candidate population.
176
                3. Population fitness values.
177
                4. Age of trees
178
        """
179
        evaluations = apply_along_axis(task.eval, 1, trees)
180
        ei = evaluations.argsort()
181
        candidates = append(candidates, trees[ei[self.al:]], axis=0)
182
        trees = trees[ei[:self.al]]
183
        age = age[ei[:self.al]]
184
        evaluations = evaluations[ei[:self.al]]
185
        return trees, candidates, evaluations, age
186
187
    def initPopulation(self, task):
188
        r"""Initialize the starting population.
189
190
        Args:
191
            task (Task): Optimization task
192
193
        Returns:
194
            Tuple[numpy.ndarray, numpy.ndarray[float], Dict[str, Any]]:
195
                1. New population.
196
                2. New population fitness/function values.
197
                3. Additional arguments:
198
                    * age (numpy.ndarray[int32]): Age of trees.
199
200
        See Also:
201
            * :func:`NiaPy.algorithms.Algorithm.initPopulation`
202
        """
203
        Trees, Evaluations, _ = Algorithm.initPopulation(self, task)
204
        age = zeros(self.NP, dtype=int32)
205
        self.dx = absolute(task.benchmark.Upper) / 5
206
        return Trees, Evaluations, {'age': age}
207
208
    def runIteration(self, task, Trees, Evaluations, xb, fxb, age, **dparams):
209
        r"""Core function of Forest Optimization Algorithm.
210
211
        Args:
212
            task (Task): Optimization task.
213
            Trees (numpy.ndarray): Current population.
214
            Evaluations (numpy.ndarray[float]): Current population function/fitness values.
215
            xb (numpy.ndarray): Global best individual.
216
            fxb (float): Global best individual fitness/function value.
217
            age (numpy.ndarray[int32]): Age of trees.
218
            **dparams (Dict[str, Any]): Additional arguments.
219
220
        Returns:
221
            Tuple[numpy.ndarray, numpy.ndarray[float], Dict[str, Any]]:
222
                1. New population.
223
                2. New population fitness/function values.
224
                3. Additional arguments:
225
                    * age (numpy.ndarray[int32]): Age of trees.
226
        """
227
        candidatePopulation = ndarray((0, task.D + 1))
228
        zeroAgeTrees = Trees[age == 0]
229
230
        localSeeds = self.localSeeding(task, zeroAgeTrees)
231
        age += 1
232
233
        Trees, candidatePopulation, age = self.removeLifeTimeExceeded(Trees, candidatePopulation, age)
234
        Trees = append(Trees, localSeeds, axis=0)
235
        age = append(age, zeros(len(localSeeds), dtype=int32))
236
        Trees, candidatePopulation, Evaluations, age = self.survivalOfTheFittest(task, Trees, candidatePopulation, age)
237
238
        gsn = int(self.tr * len(candidatePopulation))
239
        if gsn > 0:
240
            globalSeeds = self.globalSeeding(task, candidatePopulation, gsn)
241
            Trees = append(Trees, globalSeeds, axis=0)
242
            age = append(age, zeros(len(globalSeeds), dtype=int32))
243
            gste = apply_along_axis(task.eval, 1, globalSeeds)
244
            Evaluations = append(Evaluations, gste)
245
246
        ib = argmin(Evaluations)
247
        age[ib] = 0
248
        return Trees, Evaluations, {'age': age}
249