Passed
Push — master ( 7a896d...029ae0 )
by Grega
01:03
created

MonarchButterflyOptimization.adjustingOperator()   A

Complexity

Conditions 5

Size

Total Lines 30
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 15
nop 8
dl 0
loc 30
rs 9.1832
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 argsort, sum, apply_along_axis, where, pi, ceil, isinf
5
from numpy.random import exponential
6
import numpy as np
7
from NiaPy.algorithms.algorithm import Algorithm
8
9
__all__ = ['MonarchButterflyOptimization']
10
11
logging.basicConfig()
12
logger = logging.getLogger('NiaPy.algorithms.basic')
13
logger.setLevel('INFO')
14
15
16
class MonarchButterflyOptimization(Algorithm):
17
    r"""Implementation of Monarch Butterfly Optimization.
18
19
    Algorithm:
20
        Monarch Butterfly Optimization
21
22
    Date:
23
        2019
24
25
    Authors:
26
        Jan Banko
27
28
    License:
29
        MIT
30
31
    Reference paper:
32
        Wang, Gai-Ge & Deb, Suash & Cui, Zhihua. (2015). Monarch Butterfly Optimization. Neural Computing and Applications. 10.1007/s00521-015-1923-y. , https://www.researchgate.net/publication/275964443_Monarch_Butterfly_Optimization.
33
34
    Attributes:
35
        Name (List[str]): List of strings representing algorithm name.
36
        PAR (float): Partition.
37
        PER (float): Period.
38
39
    See Also:
40
        * :class:`NiaPy.algorithms.Algorithm`
41
    """
42
    Name = ['MonarchButterflyOptimization', 'MBO']
43
44
    @staticmethod
45
    def algorithmInfo():
46
        return r"""
47
        Description: Monarch butterfly optimization algorithm is inspired by the migration behaviour of the monarch butterflies in nature.
48
        Authors: Wang, Gai-Ge & Deb, Suash & Cui, Zhihua.
49
        Year: 2015
50
    Main reference: Wang, Gai-Ge & Deb, Suash & Cui, Zhihua. (2015). Monarch Butterfly Optimization. Neural Computing and Applications. 10.1007/s00521-015-1923-y. , https://www.researchgate.net/publication/275964443_Monarch_Butterfly_Optimization.
51
    """
52
53
    @staticmethod
54
    def typeParameters():
55
        r"""Get dictionary with functions for checking values of parameters.
56
57
        Returns:
58
            Dict[str, Callable]:
59
                * PAR (Callable[[float], bool]): Checks if partition parameter has a proper value.
60
                * PER (Callable[[float], bool]): Checks if period parameter has a proper value.
61
        See Also:
62
            * :func:`NiaPy.algorithms.algorithm.Algorithm.typeParameters`
63
        """
64
        d = Algorithm.typeParameters()
65
        d.update({
66
            'PAR': lambda x: isinstance(x, float) and x > 0,
67
            'PER': lambda x: isinstance(x, float) and x > 0
68
        })
69
        return d
70
71
    def setParameters(self, NP=20, PAR=5.0 / 12.0, PER=1.2, **ukwargs):
72
        r"""Set the parameters of the algorithm.
73
74
        Args:
75
            NP (Optional[int]): Population size.
76
            PAR (Optional[int]): Partition.
77
            PER (Optional[int]): Period.
78
            ukwargs (Dict[str, Any]): Additional arguments.
79
80
        See Also:
81
            * :func:`NiaPy.algorithms.Algorithm.setParameters`
82
        """
83
        Algorithm.setParameters(self, NP=NP)
84
        self.NP, self.PAR, self.PER, self.keep, self.BAR, self.NP1 = NP, PAR, PER, 2, PAR, int(ceil(PAR * NP))
85
        self.NP2 = int(NP - self.NP1)
86
        if ukwargs:
87
            logger.info('Unused arguments: %s' % (ukwargs))
88
89
    def repair(self, x, lower, upper):
90
        r"""Truncate exceeded dimensions to the limits.
91
92
        Args:
93
            x (numpy.ndarray): Individual to repair.
94
            lower (numpy.ndarray): Lower limits for dimensions.
95
            upper (numpy.ndarray): Upper limits for dimensions.
96
97
        Returns:
98
            numpy.ndarray: Repaired individual.
99
        """
100
        ir = where(x < lower)
101
        x[ir] = lower[ir]
102
        ir = where(x > upper)
103
        x[ir] = upper[ir]
104
        return x
105
106
    def levy(self, step_size, D):
107
        r"""Calculate levy flight.
108
109
        Args:
110
            step_size (float): Size of the walk step.
111
            D (int): Number of dimensions.
112
113
        Returns:
114
            numpy.ndarray: Calculated values for levy flight.
115
        """
116
        delataX = np.array(
117
            [sum(np.tan(pi * self.uniform(0.0, 1.0, 10))) for _ in range(0, D)])
118
        return delataX
119
120
    def migrationOperator(self, D, NP1, NP2, Butterflies):
121
        r"""Apply the migration operator.
122
123
        Args:
124
            D (int): Number of dimensions.
125
            NP1 (int): Number of butterflies in Land 1.
126
            NP2 (int): Number of butterflies in Land 2.
127
            Butterflies (numpy.ndarray): Current butterfly population.
128
129
        Returns:
130
            numpy.ndarray: Adjusted butterfly population.
131
        """
132
        pop1 = np.copy(Butterflies[:NP1])
133
        pop2 = np.copy(Butterflies[NP1:])
134
        for k1 in range(0, NP1):
135
            for parnum1 in range(0, D):
136
                r1 = self.uniform(0.0, 1.0) * self.PER
137
                if r1 <= self.PAR:
138
                    r2 = self.randint(Nmin=0, Nmax=NP1 - 1)
139
                    Butterflies[k1, parnum1] = pop1[r2, parnum1]
140
                else:
141
                    r3 = self.randint(Nmin=0, Nmax=NP2 - 1)
142
                    Butterflies[k1, parnum1] = pop2[r3, parnum1]
143
        return Butterflies
144
145
    def adjustingOperator(self, t, max_t, D, NP1, NP2, Butterflies, best):
146
        r"""Apply the adjusting operator.
147
148
        Args:
149
            t (int): Current generation.
150
            max_t (int): Maximum generation.
151
            D (int): Number of dimensions.
152
            NP1 (int): Number of butterflies in Land 1.
153
            NP2 (int): Number of butterflies in Land 2.
154
            Butterflies (numpy.ndarray): Current butterfly population.
155
            best (numpy.ndarray): The best butterfly currently.
156
157
        Returns:
158
            numpy.ndarray: Adjusted butterfly population.
159
        """
160
        pop2 = np.copy(Butterflies[NP1:])
161
        for k2 in range(NP1, NP1 + NP2):
162
            scale = 1.0 / ((t + 1)**2)
163
            step_size = ceil(exponential(2 * max_t))
164
            delataX = self.levy(step_size, D)
165
            for parnum2 in range(0, D):
166
                if self.uniform(0.0, 1.0) >= self.PAR:
167
                    Butterflies[k2, parnum2] = best[parnum2]
168
                else:
169
                    r4 = self.randint(Nmin=0, Nmax=NP2 - 1)
170
                    Butterflies[k2, parnum2] = pop2[r4, 1]
171
                    if(self.uniform(0.0, 1.0) > self.BAR):
172
                        Butterflies[k2, parnum2] += scale * \
173
                            (delataX[parnum2] - 0.5)
174
        return Butterflies
175
176
    def evaluateAndSort(self, task, Butterflies):
177
        r"""Evaluate and sort the butterfly population.
178
179
        Args:
180
            task (Task): Optimization task
181
            Butterflies (numpy.ndarray): Current butterfly population.
182
183
        Returns:
184
            numpy.ndarray: Tuple[numpy.ndarray, float, numpy.ndarray]:
185
                1. Best butterfly according to the evaluation.
186
                2. The best fitness value.
187
                3. Butterfly population.
188
        """
189
        Fitness = apply_along_axis(task.eval, 1, Butterflies)
190
        indices = argsort(Fitness)
191
        Butterflies = Butterflies[indices]
192
        Fitness = Fitness[indices]
193
194
        return Fitness, Butterflies
195
196
    def initPopulation(self, task):
197
        r"""Initialize the starting population.
198
199
        Args:
200
            task (Task): Optimization task
201
202
        Returns:
203
            Tuple[numpy.ndarray, numpy.ndarray[float], Dict[str, Any]]:
204
                1. New population.
205
                2. New population fitness/function values.
206
                3. Additional arguments:
207
                    * dx (float): A small value used in local seeding stage.
208
209
        See Also:
210
            * :func:`NiaPy.algorithms.Algorithm.initPopulation`
211
        """
212
        Butterflies = self.uniform(task.Lower, task.Upper, [self.NP, task.D])
213
        Fitness, Butterflies = self.evaluateAndSort(task, Butterflies)
214
        return Butterflies, Fitness, {'tmp_best': Butterflies[0]}
215
216
    def runIteration(self, task, Butterflies, Evaluations, xb, fxb, tmp_best, **dparams):
217
        r"""Core function of Forest Optimization Algorithm.
218
219
        Args:
220
            task (Task): Optimization task.
221
            Butterflies (numpy.ndarray): Current population.
222
            Evaluations (numpy.ndarray[float]): Current population function/fitness values.
223
            xb (numpy.ndarray): Global best individual.
224
            fxb (float): Global best individual fitness/function value.
225
            tmp_best (numpy.ndarray): Best individual currently.
226
            **dparams (Dict[str, Any]): Additional arguments.
227
228
        Returns:
229
            Tuple[numpy.ndarray, numpy.ndarray[float], Dict[str, Any]]:
230
                1. New population.
231
                2. New population fitness/function values.
232
                3. Additional arguments:
233
                    * dx (float): A small value used in local seeding stage.
234
        """
235
        tmpElite = np.copy(Butterflies[:self.keep])
236
        max_t = task.nGEN if isinf(task.nGEN) is False else task.nFES / self.NP
237
238
        Butterflies = apply_along_axis(self.repair, 1, self.migrationOperator(
239
            task.D, self.NP1, self.NP2, Butterflies), task.Lower, task.Upper)
240
        Butterflies = apply_along_axis(self.repair, 1, self.adjustingOperator(
241
            task.Iters, max_t, task.D, self.NP1, self.NP2, Butterflies, tmp_best), task.Lower, task.Upper)
242
        Fitness, Butterflies = self.evaluateAndSort(task, Butterflies)
243
        tmp_best = Butterflies[0]
244
        Butterflies[-self.keep:] = tmpElite
245
        Fitness, Butterflies = self.evaluateAndSort(task, Butterflies)
246
        return Butterflies, Fitness, {'tmp_best': tmp_best}
247