MonarchButterflyOptimization.typeParameters()   A
last analyzed

Complexity

Conditions 3

Size

Total Lines 17
Code Lines 7

Duplication

Lines 17
Ratio 100 %

Importance

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