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

ForestOptimizationAlgorithm.setParameters()   A

Complexity

Conditions 2

Size

Total Lines 18
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nop 8
dl 0
loc 18
rs 10
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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