Passed
Pull Request — master (#240)
by
unknown
01:19
created

SelfAdaptiveBatAlgorithm.runIteration()   B

Complexity

Conditions 6

Size

Total Lines 37
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 11
nop 12
dl 0
loc 37
rs 8.6666
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
import logging
3
4
import numpy as np
5
6
from NiaPy.algorithms.algorithm import Algorithm
7
8
logging.basicConfig()
9
logger = logging.getLogger('NiaPy.algorithms.modified')
10
logger.setLevel('INFO')
11
12
__all__ = ['AdaptiveBatAlgorithm', 'SelfAdaptiveBatAlgorithm']
13
14
class AdaptiveBatAlgorithm(Algorithm):
15
	r"""Implementation of Adaptive bat algorithm.
16
17
	Algorithm:
18
		Adaptive bat algorithm
19
20
	Date:
21
		April 2019
22
23
	Authors:
24
		Klemen Berkovič
25
26
	License:
27
		MIT
28
29
	Attributes:
30
		Name (List[str]): List of strings representing algorithm name.
31
		epsilon (float): Scaling factor.
32
		alpha (float): Constant for updating loudness.
33
		r (float): Pulse rate.
34
		Qmin (float): Minimum frequency.
35
		Qmax (float): Maximum frequency.
36
37
	See Also:
38
		* :class:`NiaPy.algorithms.Algorithm`
39
	"""
40
	Name = ['AdaptiveBatAlgorithm', 'ABA']
41
42
	@staticmethod
43
	def typeParameters():
44
		r"""Return dict with where key of dict represents parameter name and values represent checking functions for selected parameter.
45
46
		Returns:
47
			Dict[str, Callable]:
48
				* epsilon (Callable[[Union[float, int]], bool]): Scale factor.
49
				* alpha (Callable[[Union[float, int]], bool]): Constant for updating loudness.
50
				* r (Callable[[Union[float, int]], bool]): Pulse rate.
51
				* Qmin (Callable[[Union[float, int]], bool]): Minimum frequency.
52
				* Qmax (Callable[[Union[float, int]], bool]): Maximum frequency.
53
54
		See Also:
55
			* :func:`NiaPy.algorithms.Algorithm.typeParameters`
56
		"""
57
		d = Algorithm.typeParameters()
58
		d.update({
59
			'epsilon': lambda x: isinstance(x, (float, int)) and x > 0,
60
			'alpha': lambda x: isinstance(x, (float, int)) and x > 0,
61
			'r': lambda x: isinstance(x, (float, int)) and x > 0,
62
			'Qmin': lambda x: isinstance(x, (float, int)),
63
			'Qmax': lambda x: isinstance(x, (float, int))
64
		})
65
		return d
66
67
	def setParameters(self, NP=100, A=0.5, epsilon=0.001, alpha=1.0, r=0.5, Qmin=0.0, Qmax=2.0, **ukwargs):
68
		r"""Set the parameters of the algorithm.
69
70
		Args:
71
			A (Optional[float]): Starting loudness.
72
			epsilon (Optional[float]): Scaling factor.
73
			alpha (Optional[float]): Constant for updating loudness.
74
			r (Optional[float]): Pulse rate.
75
			Qmin (Optional[float]): Minimum frequency.
76
			Qmax (Optional[float]): Maximum frequency.
77
78
		See Also:
79
			* :func:`NiaPy.algorithms.Algorithm.setParameters`
80
		"""
81
		Algorithm.setParameters(self, NP=NP, **ukwargs)
82
		self.A, self.epsilon, self.alpha, self.r, self.Qmin, self.Qmax = A, epsilon, alpha, r, Qmin, Qmax
83
84
	def getParameters(self):
85
		r"""Get algorithm parameters.
86
87
		Returns:
88
			Dict[str, Any]: Arguments values.
89
90
		See Also:
91
			* :func:`NiaPy.algorithms.algorithm.Algorithm.getParameters`
92
		"""
93
		d = Algorithm.getParameters(self)
94
		d.update({
95
			'A': self.A,
96
			'epsilon': self.epsilon,
97
			'alpha': self.alpha,
98
			'r': self.r,
99
			'Qmin': self.Qmin,
100
			'Qmax': self.Qmax
101
		})
102
		return d
103
104 View Code Duplication
	def initPopulation(self, task):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
105
		r"""Initialize the starting population.
106
107
		Parameters:
108
			task (Task): Optimization task
109
110
		Returns:
111
			Tuple[numpy.ndarray, numpy.ndarray[float], Dict[str, Any]]:
112
				1. New population.
113
				2. New population fitness/function values.
114
				3. Additional arguments:
115
					* A (float): Loudness.
116
					* S (numpy.ndarray): TODO
117
					* Q (numpy.ndarray[float]): 	TODO
118
					* v (numpy.ndarray[float]): TODO
119
120
		See Also:
121
			* :func:`NiaPy.algorithms.Algorithm.initPopulation`
122
		"""
123
		Sol, Fitness, d = Algorithm.initPopulation(self, task)
124
		A, S, Q, v = np.full(self.NP, self.A), np.full([self.NP, task.D], 0.0), np.full(self.NP, 0.0), np.full([self.NP, task.D], 0.0)
125
		d.update({'A': A, 'S': S, 'Q': Q, 'v': v})
126
		return Sol, Fitness, d
127
128
	def localSearch(self, best, A, task, **kwargs):
129
		r"""Improve the best solution according to the Yang (2010).
130
131
		Args:
132
			best (numpy.ndarray): Global best individual.
133
			A (float): Loudness.
134
			task (Task): Optimization task.
135
			**kwargs (Dict[str, Any]): Additional arguments.
136
137
		Returns:
138
			numpy.ndarray: New solution based on global best individual.
139
		"""
140
		return task.repair(best + self.epsilon * A * self.normal(0, 1, task.D), rnd=self.Rand)
141
142
	def updateLoudness(self, A):
143
		r"""Update loudness when the prey is found.
144
145
		Args:
146
			A (float): Loudness.
147
148
		Returns:
149
			float: New loudness.
150
		"""
151
		nA = A * self.alpha
152
		return nA if nA > 1e-13 else self.A
153
154
	def runIteration(self, task, Sol, Fitness, xb, fxb, A, S, Q, v, **dparams):
155
		r"""Core function of Bat Algorithm.
156
157
		Parameters:
158
			task (Task): Optimization task.
159
			Sol (numpy.ndarray): Current population
160
			Fitness (numpy.ndarray[float]): Current population fitness/funciton values
161
			best (numpy.ndarray): Current best individual
162
			f_min (float): Current best individual function/fitness value
163
			S (numpy.ndarray): TODO
164
			Q (numpy.ndarray[float]): TODO
165
			v (numpy.ndarray[float]): TODO
166
			dparams (Dict[str, Any]): Additional algorithm arguments
167
168
		Returns:
169
			Tuple[numpy.ndarray, numpy.ndarray[float], Dict[str, Any]]:
170
				1. New population
171
				2. New population fitness/function vlues
172
				3. Additional arguments:
173
					* A (numpy.ndarray[float]): Loudness.
174
					* S (numpy.ndarray): TODO
175
					* Q (numpy.ndarray[float]): TODO
176
					* v (numpy.ndarray[float]): TODO
177
		"""
178
		for i in range(self.NP):
179
			Q[i] = self.Qmin + (self.Qmax - self.Qmin) * self.uniform(0, 1)
180
			v[i] += (Sol[i] - xb) * Q[i]
181
			if self.rand() > self.r: S[i] = self.localSearch(best=xb, A=A[i], task=task, i=i, Sol=Sol)
182
			else: S[i] = task.repair(Sol[i] + v[i], rnd=self.Rand)
183
			Fnew = task.eval(S[i])
184
			if (Fnew <= Fitness[i]) and (self.rand() < A[i]): Sol[i], Fitness[i] = S[i], Fnew
185
			if Fnew <= fxb: xb, fxb, A[i] = S[i].copy(), Fnew, self.updateLoudness(A[i])
186
		return Sol, Fitness, xb, fxb, {'A': A, 'S': S, 'Q': Q, 'v': v}
187
188
class SelfAdaptiveBatAlgorithm(AdaptiveBatAlgorithm):
189
	r"""Implementation of Hybrid bat algorithm.
190
191
	Algorithm:
192
		Hybrid bat algorithm
193
194
	Date:
195
		April 2019
196
197
	Author:
198
		Klemen Berkovič
199
200
	License:
201
		MIT
202
203
	Reference paper:
204
		Fister Jr., Iztok and Fister, Dusan and Yang, Xin-She. "A Hybrid Bat Algorithm". Elektrotehniski vestnik, 2013. 1-7.
205
206
	Attributes:
207
		Name (List[str]): List of strings representing algorithm name.
208
		A_l (Optional[float]): Lower limit of loudness.
209
		A_u (Optional[float]): Upper limit of loudness.
210
		r_l (Optional[float]): Lower limit of pulse rate.
211
		r_u (Optional[float]): Upper limit of pulse rate.
212
		tao_1 (Optional[float]): Learning rate for loudness.
213
		tao_2 (Optional[float]): Learning rate for pulse rate.
214
215
	See Also:
216
		* :class:`NiaPy.algorithms.basic.BatAlgorithm`
217
	"""
218
	Name = ['SelfAdaptiveBatAlgorithm', 'SABA']
219
220
	@staticmethod
221
	def algorithmInfo():
222
		r"""Get basic information about the algorithm.
223
224
		Returns:
225
			str: Basic information.
226
		"""
227
		return r"""Fister Jr., Iztok and Fister, Dusan and Yang, Xin-She. "A Hybrid Bat Algorithm". Elektrotehniski vestnik, 2013. 1-7."""
228
229 View Code Duplication
	@staticmethod
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
230
	def typeParameters():
231
		r"""Get dictionary with functions for checking values of parameters.
232
233
		Returns:
234
			Dict[str, Callable]: TODO
235
236
		See Also:
237
			* :func:`NiaPy.algorithms.basic.BatAlgorithm.typeParameters`
238
		"""
239
		d = AdaptiveBatAlgorithm.typeParameters()
240
		d.pop('A', None), d.pop('r', None)
241
		d.update({
242
			'A_l': lambda x: isinstance(x, (float, int)) and x >= 0,
243
			'A_u': lambda x: isinstance(x, (float, int)) and x >= 0,
244
			'r_l': lambda x: isinstance(x, (float, int)) and x >= 0,
245
			'r_u': lambda x: isinstance(x, (float, int)) and x >= 0,
246
			'tao_1': lambda x: isinstance(x, (float, int)) and 0 <= x <= 1,
247
			'tao_2': lambda x: isinstance(x, (float, int)) and 0 <= x <= 1
248
		})
249
		return d
250
251
	def setParameters(self, A_l=0.001, A_u=0.7, r_l=0.1, r_u=0.09, tao_1=0.3, tao_2=0.5, **ukwargs):
252
		r"""Set core parameters of HybridBatAlgorithm algorithm.
253
254
		Arguments:
255
			A_l (Optional[float]): Lower limit of loudness.
256
			A_u (Optional[float]): Upper limit of loudness.
257
			r_l (Optional[float]): Lower limit of pulse rate.
258
			r_u (Optional[float]): Upper limit of pulse rate.
259
			tao_1 (Optional[float]): Learning rate for loudness.
260
			tao_2 (Optional[float]): Learning rate for pulse rate.
261
262
		See Also:
263
			* :func:`NiaPy.algorithms.modified.AdaptiveBatAlgorithm.setParameters`
264
		"""
265
		AdaptiveBatAlgorithm.setParameters(self, **ukwargs)
266
		self.A_l, self.A_u, self.r_l, self.r_u, self.tao_1, self.tao_2 = A_l, A_u, r_l, r_u, tao_1, tao_2
267
268
	def getParameters(self):
269
		r"""Get parameters of the algorithm.
270
271
		Returns:
272
			Dict[str, Any]: Parameters of the algorithm.
273
274
		See Also:
275
			* :func:`NiaPy.algorithms.modified.AdaptiveBatAlgorithm.getParameters`
276
		"""
277
		d = AdaptiveBatAlgorithm.getParameters(self)
278
		d.pop('A', None), d.pop('r', None)
279
		d.update({
280
			'A_l': self.A_l,
281
			'A_u': self.A_u,
282
			'r_l': self.r_l,
283
			'r_u': self.r_u,
284
			'tao_1': self.tao_1,
285
			'tao_2': self.tao_2
286
		})
287
		return d
288
289
	def initPopulation(self, task):
290
		Sol, Fitness, d = AdaptiveBatAlgorithm.initPopulation(self, task)
291
		A, r = self.A_l + self.rand(self.NP) * (self.A_u - self.A_l), self.r_l + self.rand(self.NP) * (self.r_u - self.r_l)
292
		d.pop('A', None)
293
		d.update({'A': A, 'r': r})
294
		return Sol, Fitness, d
295
296
	def selfAdaptation(self, A, r):
297
		r"""Adaptation step.
298
299
		Args:
300
			A (float): Current loudness.
301
			r (float): Current pulse rate.
302
303
		Returns:
304
			Tuple[float, float]:
305
				1. New loudness.
306
				2. Nwq pulse rate.
307
		"""
308
		return self.A_l + self.rand() * (self.A_u - self.A_l) if self.rand() < self.tao_1 else A, self.r_l + self.rand() * (self.r_u - self.r_l) if self.rand() < self.tao_2 else r
309
310
	def runIteration(self, task, Sol, Fitness, xb, fxb, A, r, S, Q, v, **dparams):
311
		r"""Core function of Bat Algorithm.
312
313
		Parameters:
314
			task (Task): Optimization task.
315
			Sol (numpy.ndarray): Current population
316
			Fitness (numpy.ndarray[float]): Current population fitness/funciton values
317
			xb (numpy.ndarray): Current best individual
318
			fxb (float): Current best individual function/fitness value
319
			A (numpy.ndarray[flaot]): Loudness of individuals.
320
			r (numpy.ndarray[float[): Pulse rate of individuals.
321
			S (numpy.ndarray): TODO
322
			Q (numpy.ndarray[float]): TODO
323
			v (numpy.ndarray[float]): TODO
324
			dparams (Dict[str, Any]): Additional algorithm arguments
325
326
		Returns:
327
			Tuple[numpy.ndarray, numpy.ndarray[float], Dict[str, Any]]:
328
				1. New population
329
				2. New population fitness/function vlues
330
				3. Additional arguments:
331
					* A (numpy.ndarray[float]): Loudness.
332
					* r (numpy.ndarray[float]): Pulse rate.
333
					* S (numpy.ndarray): TODO
334
					* Q (numpy.ndarray[float]): TODO
335
					* v (numpy.ndarray[float]): TODO
336
		"""
337
		for i in range(self.NP):
338
			A[i], r[i] = self.selfAdaptation(A[i], r[i])
339
			Q[i] = self.Qmin + (self.Qmax - self.Qmin) * self.uniform(0, 1)
340
			v[i] += (Sol[i] - xb) * Q[i]
341
			if self.rand() > r[i]: S[i] = self.localSearch(best=xb, A=A[i], task=task, i=i, Sol=Sol)
342
			else: S[i] = task.repair(Sol[i] + v[i], rnd=self.Rand)
343
			Fnew = task.eval(S[i])
344
			if (Fnew <= Fitness[i]) and (self.rand() < (self.A_l - A[i]) / self.A): Sol[i], Fitness[i] = S[i], Fnew
345
			if Fnew <= fxb: xb, fxb = S[i].copy(), Fnew
346
		return Sol, Fitness, xb, fxb, {'A': A, 'r': r, 'S': S, 'Q': Q, 'v': v}
347
348
# vim: tabstop=3 noexpandtab shiftwidth=3 softtabstop=3
349