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

MonarchButterflyOptimization.migrationOperator()   A

Complexity

Conditions 4

Size

Total Lines 24
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 12
nop 5
dl 0
loc 24
rs 9.8
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 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