Completed
Push — master ( cc7745...279fb5 )
by Grega
19s queued 17s
created

SelfAdaptiveBatAlgorithm.setParameters()   A

Complexity

Conditions 1

Size

Total Lines 16
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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