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

AdaptiveBatAlgorithm.initPopulation()   A

Complexity

Conditions 1

Size

Total Lines 23
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
nop 2
dl 0
loc 23
rs 10
c 0
b 0
f 0
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