NiaPy.algorithms.basic.mke   A
last analyzed

Complexity

Total Complexity 40

Size/Duplication

Total Lines 478
Duplicated Lines 4.18 %

Importance

Changes 0
Metric Value
wmc 40
eloc 121
dl 20
loc 478
rs 9.2
c 0
b 0
f 0

22 Methods

Rating   Name   Duplication   Size   Complexity  
A MonkeyKingEvolutionV1.moveP() 0 18 1
A MonkeyKingEvolutionV3.neg() 0 10 2
A MonkeyKingEvolutionV3.initPopulation() 0 21 1
A MkeSolution.__init__() 0 12 1
A MonkeyKingEvolutionV3.setParameters() 0 10 1
A MonkeyKingEvolutionV1.algorithmInfo() 0 11 1
A MonkeyKingEvolutionV1.getParameters() 0 17 1
A MonkeyKingEvolutionV2.moveMK() 0 15 1
A MonkeyKingEvolutionV1.movePartice() 0 10 1
A MonkeyKingEvolutionV2.moveMokeyKingPartice() 0 16 3
A MonkeyKingEvolutionV3.algorithmInfo() 0 11 1
A MonkeyKingEvolutionV2.movePopulation() 0 16 3
A MonkeyKingEvolutionV1.initPopulation() 0 15 2
A MonkeyKingEvolutionV1.runIteration() 0 22 2
A MonkeyKingEvolutionV2.algorithmInfo() 0 11 1
A MonkeyKingEvolutionV1.setParameters() 0 16 1
A MonkeyKingEvolutionV1.moveMokeyKingPartice() 0 12 1
B MonkeyKingEvolutionV1.typeParameters() 20 20 6
A MkeSolution.uPersonalBest() 0 3 2
A MonkeyKingEvolutionV1.moveMK() 0 16 1
A MonkeyKingEvolutionV1.movePopulation() 0 16 3
A MonkeyKingEvolutionV3.runIteration() 0 33 4

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complexity

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like NiaPy.algorithms.basic.mke often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
# encoding=utf8
2
import logging
3
from math import ceil
4
5
from numpy import apply_along_axis, vectorize, argmin, argmax, full, tril, asarray
6
7
from NiaPy.algorithms.algorithm import Algorithm, Individual, defaultIndividualInit, defaultNumPyInit
8
9
logging.basicConfig()
10
logger = logging.getLogger('NiaPy.algorithms.basic')
11
logger.setLevel('INFO')
12
13
__all__ = ['MonkeyKingEvolutionV1', 'MonkeyKingEvolutionV2', 'MonkeyKingEvolutionV3']
14
15
class MkeSolution(Individual):
16
	r"""Implementation of Monkey King Evolution individual.
17
18
	Data:
19
		2018
20
21
	Authors:
22
		Klemen Berkovič
23
24
	License:
25
		MIT
26
27
	Attributes:
28
		x_pb (array of (float or int)): Personal best position of Monkey particle.
29
		f_pb (float): Personal best fitness/function value.
30
		MonkeyKing (bool): Boolean value indicating if particle is Monkey King particle.
31
32
	See Also:
33
		* :class:`NiaPy.algorithms.Individual`
34
	"""
35
	def __init__(self, **kwargs):
36
		r"""Initialize Monkey particle.
37
38
		Args:
39
			**kwargs: Additional arguments
40
41
		See Also:
42
			* :class:`NiaPy.algorithms.Individual.__init__()`
43
		"""
44
		Individual.__init__(self, **kwargs)
45
		self.f_pb, self.x_pb = self.f, self.x
46
		self.MonkeyKing = False
47
48
	def uPersonalBest(self):
49
		r"""Update presonal best position of particle."""
50
		if self.f < self.f_pb: self.x_pb, self.f_pb = self.x, self.f
51
52
class MonkeyKingEvolutionV1(Algorithm):
53
	r"""Implementation of monkey king evolution algorithm version 1.
54
55
	Algorithm:
56
		Monkey King Evolution version 1
57
58
	Date:
59
		2018
60
61
	Authors:
62
		Klemen Berkovič
63
64
	License:
65
		MIT
66
67
	Reference URL:
68
		https://www.sciencedirect.com/science/article/pii/S0950705116000198
69
70
	Reference paper:
71
		Zhenyu Meng, Jeng-Shyang Pan, Monkey King Evolution: A new memetic evolutionary algorithm and its application in vehicle fuel consumption optimization, Knowledge-Based Systems, Volume 97, 2016, Pages 144-157, ISSN 0950-7051, https://doi.org/10.1016/j.knosys.2016.01.009.
72
73
	Attributes:
74
		Name (List[str]): List of strings representing algorithm names.
75
		F (float): Scale factor for normal particles.
76
		R (float): TODO.
77
		C (int): Number of new particles generated by Monkey King particle.
78
		FC (float): Scale factor for Monkey King particles.
79
80
	See Also:
81
		* :class:`NiaPy.algorithms.algorithm.Algorithm`
82
	"""
83
	Name = ['MonkeyKingEvolutionV1', 'MKEv1']
84
85
	@staticmethod
86
	def algorithmInfo():
87
		r"""Get basic information of algorithm.
88
89
		Returns:
90
			str: Basic information.
91
92
		See Also:
93
			* :func:`NiaPy.algorithms.Algorithm.algorithmInfo`
94
		"""
95
		return r"""Zhenyu Meng, Jeng-Shyang Pan, Monkey King Evolution: A new memetic evolutionary algorithm and its application in vehicle fuel consumption optimization, Knowledge-Based Systems, Volume 97, 2016, Pages 144-157, ISSN 0950-7051, https://doi.org/10.1016/j.knosys.2016.01.009."""
96
97 View Code Duplication
	@staticmethod
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
98
	def typeParameters():
99
		r"""Get dictionary with functions for checking values of parameters.
100
101
		Returns:
102
			Dict[str, Callable]:
103
				* F (Callable[[int], bool])
104
				* R (Callable[[Union[int, float]], bool])
105
				* C (Callable[[Union[int, float]], bool])
106
				* FC (Callable[[Union[int, float]], bool])
107
		"""
108
		d = Algorithm.typeParameters()
109
		d.update({
110
			'NP': lambda x: isinstance(x, int) and x > 0,
111
			'F': lambda x: isinstance(x, (float, int)) and x > 0,
112
			'R': lambda x: isinstance(x, (float, int)) and x > 0,
113
			'C': lambda x: isinstance(x, int) and x > 0,
114
			'FC': lambda x: isinstance(x, (float, int)) and x > 0
115
		})
116
		return d
117
118
	def setParameters(self, NP=40, F=0.7, R=0.3, C=3, FC=0.5, **ukwargs):
119
		r"""Set Monkey King Evolution v1 algorithms static parameters.
120
121
		Args:
122
			NP (int): Population size.
123
			F (float): Scale factor for normal particle.
124
			R (float): Procentual value of now many new particle Monkey King particle creates. Value in rage [0, 1].
125
			C (int): Number of new particles generated by Monkey King particle.
126
			FC (float): Scale factor for Monkey King particles.
127
			**ukwargs (Dict[str, Any]): Additional arguments.
128
129
		See Also:
130
			* :func:`NiaPy.algorithms.algorithm.Algorithm.setParameters`
131
		"""
132
		Algorithm.setParameters(self, NP=NP, itype=ukwargs.pop('itype', MkeSolution), InitPopFunc=ukwargs.pop('InitPopFunc', defaultIndividualInit), **ukwargs)
133
		self.F, self.R, self.C, self.FC = F, R, C, FC
134
135
	def getParameters(self):
136
		r"""Get algorithms parametes values.
137
138
		Returns:
139
			Dict[str, Any]
140
141
		See Also:
142
			* :func:`NiaPy.algorithms.Algorithm.getParameters`
143
		"""
144
		d = Algorithm.getParameters(self)
145
		d.update({
146
			'F': self.F,
147
			'R': self.R,
148
			'C': self.C,
149
			'FC': self.FC
150
		})
151
		return d
152
153
	def moveP(self, x, x_pb, x_b, task):
154
		r"""Move normal particle in search space.
155
156
		For moving particles algorithm uses next formula:
157
		:math:`\mathbf{x_{pb} - \mathit{F} \odot \mathbf{r} \odot (\mathbf{x_b} - \mathbf{x})`
158
		where
159
		:math:`\mathbf{r}` is one dimension array with `D` components. Components in this vector are in range [0, 1].
160
161
		Args:
162
			x (numpy.ndarray): Paticle position.
163
			x_pb (numpy.ndarray): Particle best position.
164
			x_b (numpy.ndarray): Best particle position.
165
			task (Task): Optimization task.
166
167
		Returns:
168
			numpy.ndarray: Particle new position.
169
		"""
170
		return x_pb + self.F * self.rand(task.D) * (x_b - x)
171
172
	def moveMK(self, x, task):
173
		r"""Move Mokey King paticle.
174
175
		For moving Monkey King particles algorithm uses next formula:
176
		:math:`\mathbf{x} + \mathit{FC} \odot \mathbf{R} \odot \mathbf{x}`
177
		where
178
		:math:`\mathbf{R}` is two dimensional array with shape `{C * D, D}`. Componentes of this array are in range [0, 1]
179
180
		Args:
181
			x (numpy.ndarray): Monkey King patricle position.
182
			task (Task): Optimization task.
183
184
		Returns:
185
			numpy.ndarray: New particles generated by Monkey King particle.
186
		"""
187
		return x + self.FC * self.rand([int(self.C * task.D), task.D]) * x
188
189
	def movePartice(self, p, p_b, task):
190
		r"""Move patricles.
191
192
		Args:
193
			p (MkeSolution): Monke particle.
194
			p_b (MkeSolution): Population best particle.
195
			task (Task): Optimization task.
196
		"""
197
		p.x = self.moveP(p.x, p.x_pb, p_b, task)
198
		p.evaluate(task, rnd=self.Rand)
199
200
	def moveMokeyKingPartice(self, p, task):
201
		r"""Move Monky King Particles.
202
203
		Args:
204
			p (MkeSolution): Monkey King particle to apply this function on.
205
			task (Task): Optimization task.
206
		"""
207
		p.MonkeyKing = False
208
		A = apply_along_axis(task.repair, 1, self.moveMK(p.x, task), self.Rand)
209
		A_f = apply_along_axis(task.eval, 1, A)
210
		ib = argmin(A_f)
211
		p.x, p.f = A[ib], A_f[ib]
212
213
	def movePopulation(self, pop, xb, task):
214
		r"""Move population.
215
216
		Args:
217
			pop (numpy.ndarray[MkeSolution]): Current population.
218
			xb (MkeSolution): Current best solution.
219
			task (Task): Optimization task.
220
221
		Returns:
222
			numpy.ndarray[MkeSolution]: New particles.
223
		"""
224
		for p in pop:
225
			if p.MonkeyKing: self.moveMokeyKingPartice(p, task)
226
			else: self.movePartice(p, xb, task)
227
			p.uPersonalBest()
228
		return pop
229
230
	def initPopulation(self, task):
231
		r"""Init population.
232
233
		Args:
234
			task (Task): Optimization task
235
236
		Returns:
237
			Tuple(numpy.ndarray[MkeSolution], numpy.ndarray[float], Dict[str, Any]]:
238
				1. Initialized solutions
239
				2. Fitness/function values of solution
240
				3. Additional arguments
241
		"""
242
		pop, fpop, _ = Algorithm.initPopulation(self, task)
243
		for i in self.Rand.choice(self.NP, int(self.R * len(pop)), replace=False): pop[i].MonkeyKing = True
244
		return pop, fpop, {}
245
246
	def runIteration(self, task, pop, fpop, xb, fxb, **dparams):
247
		r"""Core function of Monkey King Evolution v1 algorithm.
248
249
		Args:
250
			task (Task): Optimization task.
251
			pop (numpy.ndarray[MkeSolution]): Current population.
252
			fpop (numpy.ndarray[float]): Current population fitness/function values.
253
			xb (MkeSolution): Current best solution.
254
			fxb (float): Current best solutions function/fitness value.
255
			**dparams (Dict[str, Any]): Additional arguments.
256
257
		Returns:
258
			Tuple(numpy.ndarray[MkeSolution], numpy.ndarray[float], Dict[str, Any]]:
259
				1. Initialized solutions.
260
				2. Fitness/function values of solution.
261
				3. Additional arguments.
262
		"""
263
		pop = self.movePopulation(pop, xb, task)
264
		for i in self.Rand.choice(self.NP, int(self.R * len(pop)), replace=False): pop[i].MonkeyKing = True
265
		fpop = asarray([m.f for m in pop])
266
		xb, fxb = self.getBest(pop, fpop, xb, fxb)
267
		return pop, fpop, xb, fxb, {}
268
269
class MonkeyKingEvolutionV2(MonkeyKingEvolutionV1):
270
	r"""Implementation of monkey king evolution algorithm version 2.
271
272
	Algorithm:
273
		Monkey King Evolution version 2
274
275
	Date:
276
		2018
277
278
	Authors:
279
		Klemen Berkovič
280
281
	License:
282
		MIT
283
284
	Reference URL:
285
		https://www.sciencedirect.com/science/article/pii/S0950705116000198
286
287
	Reference paper:
288
		Zhenyu Meng, Jeng-Shyang Pan, Monkey King Evolution: A new memetic evolutionary algorithm and its application in vehicle fuel consumption optimization, Knowledge-Based Systems, Volume 97, 2016, Pages 144-157, ISSN 0950-7051, https://doi.org/10.1016/j.knosys.2016.01.009.
289
290
	Attributes:
291
		Name (List[str]): List of strings representing algorithm names.
292
293
	See Also:
294
		* :class:`NiaPy.algorithms.basic.mke.MonkeyKingEvolutionV1`
295
	"""
296
	Name = ['MonkeyKingEvolutionV2', 'MKEv2']
297
298
	@staticmethod
299
	def algorithmInfo():
300
		r"""Get basic information of algorithm.
301
302
		Returns:
303
			str: Basic information.
304
305
		See Also:
306
			* :func:`NiaPy.algorithms.Algorithm.algorithmInfo`
307
		"""
308
		return r"""Zhenyu Meng, Jeng-Shyang Pan, Monkey King Evolution: A new memetic evolutionary algorithm and its application in vehicle fuel consumption optimization, Knowledge-Based Systems, Volume 97, 2016, Pages 144-157, ISSN 0950-7051, https://doi.org/10.1016/j.knosys.2016.01.009."""
309
310
	def moveMK(self, x, dx, task):
311
		r"""Move Monkey King particle.
312
313
		For movment of particles algorithm uses next formula:
314
		:math:`\mathbf{x} - \mathit{FC} \odot \mathbf{dx}`
315
316
		Args:
317
			x (numpy.ndarray): Particle to apply movment on.
318
			dx (numpy.ndarray): Difference between to random paricles in population.
319
			task (Task): Optimization task.
320
321
		Returns:
322
			numpy.ndarray: Moved particles.
323
		"""
324
		return x - self.FC * dx
325
326
	def moveMokeyKingPartice(self, p, pop, task):
327
		r"""Move Monkey King particles.
328
329
		Args:
330
			p (MkeSolution): Monkey King particle to move.
331
			pop (numpy.ndarray[MkeSolution]): Current population.
332
			task (Task): Optimization task.
333
		"""
334
		p.MonkeyKing = False
335
		p_b, p_f = p.x, p.f
336
		for _i in range(int(self.C * self.NP)):
337
			r = self.Rand.choice(self.NP, 2, replace=False)
338
			a = task.repair(self.moveMK(p.x, pop[r[0]].x - pop[r[1]].x, task), self.Rand)
339
			a_f = task.eval(a)
340
			if a_f < p_f: p_b, p_f = a, a_f
341
		p.x, p.f = p_b, p_f
342
343
	def movePopulation(self, pop, xb, task):
344
		r"""Move population.
345
346
		Args:
347
			pop (numpy.ndarray[MkeSolution]): Current population.
348
			xb (MkeSolution): Current best solution.
349
			task (Task): Optimization task.
350
351
		Returns:
352
			numpy.ndarray[MkeSolution]: Moved population.
353
		"""
354
		for p in pop:
355
			if p.MonkeyKing: self.moveMokeyKingPartice(p, pop, task)
356
			else: self.movePartice(p, xb, task)
357
			p.uPersonalBest()
358
		return pop
359
360
class MonkeyKingEvolutionV3(MonkeyKingEvolutionV1):
361
	r"""Implementation of monkey king evolution algorithm version 3.
362
363
	Algorithm:
364
		Monkey King Evolution version 3
365
366
	Date:
367
		2018
368
369
	Authors:
370
		Klemen Berkovič
371
372
	License:
373
		MIT
374
375
	Reference URL:
376
		https://www.sciencedirect.com/science/article/pii/S0950705116000198
377
378
	Reference paper:
379
		Zhenyu Meng, Jeng-Shyang Pan, Monkey King Evolution: A new memetic evolutionary algorithm and its application in vehicle fuel consumption optimization, Knowledge-Based Systems, Volume 97, 2016, Pages 144-157, ISSN 0950-7051, https://doi.org/10.1016/j.knosys.2016.01.009.
380
381
	Attributes:
382
		Name (List[str]): List of strings that represent algorithm names.
383
384
	See Also:
385
		* :class:`NiaPy.algorithms.basic.mke.MonkeyKingEvolutionV1`
386
	"""
387
	Name = ['MonkeyKingEvolutionV3', 'MKEv3']
388
389
	@staticmethod
390
	def algorithmInfo():
391
		r"""Get basic information of algorithm.
392
393
		Returns:
394
			str: Basic information.
395
396
		See Also:
397
			* :func:`NiaPy.algorithms.Algorithm.algorithmInfo`
398
		"""
399
		return r"""Zhenyu Meng, Jeng-Shyang Pan, Monkey King Evolution: A new memetic evolutionary algorithm and its application in vehicle fuel consumption optimization, Knowledge-Based Systems, Volume 97, 2016, Pages 144-157, ISSN 0950-7051, https://doi.org/10.1016/j.knosys.2016.01.009."""
400
401
	def setParameters(self, **ukwargs):
402
		r"""Set core parameters of MonkeyKingEvolutionV3 algorithm.
403
404
		Args:
405
			**ukwargs (Dict[str, Any]): Additional arguments.
406
407
		See Also:
408
			* :func:`NiaPy.algorithms.basic.MonkeyKingEvolutionV1.setParameters`
409
		"""
410
		MonkeyKingEvolutionV1.setParameters(self, itype=ukwargs.pop('itype', None), InitPopFunc=ukwargs.pop('InitPopFunc', defaultNumPyInit), **ukwargs)
411
412
	def neg(self, x):
413
		r"""Transform function.
414
415
		Args:
416
			x (Union[int, float]): Sould be 0 or 1.
417
418
		Returns:
419
			float: If 0 thet 1 else 1 then 0.
420
		"""
421
		return 0.0 if x == 1.0 else 1.0
422
423
	def initPopulation(self, task):
424
		r"""Initialize the population.
425
426
		Args:
427
			task (Task): Optimization task.
428
429
		Returns:
430
			Tuple[numpy.ndarray, numpy.ndarray[float], Dict[str, Any]]:
431
				1. Initialized population.
432
				2. Initialized population function/fitness values.
433
				3. Additional arguments:
434
					* k (int): TODO.
435
					* c (int): TODO.
436
437
		See Also:
438
			* :func:`NiaPy.algorithms.algorithm.Algorithm.initPopulation`
439
		"""
440
		X, X_f, d = Algorithm.initPopulation(self, task)
441
		k, c = int(ceil(self.NP / task.D)), int(ceil(self.C * task.D))
442
		d.update({'k': k, 'c': c})
443
		return X, X_f, d
444
445
	def runIteration(self, task, X, X_f, xb, fxb, k, c, **dparams):
446
		r"""Core funciton of Monkey King Evolution v3 algorithm.
447
448
		Args:
449
			task (Task): Optimization task.
450
			X (numpy.ndarray): Current population.
451
			X_f (numpy.ndarray[float]): Current population fitness/function values.
452
			xb (numpy.ndarray): Current best individual.
453
			fxb (float): Current best individual function/fitness value.
454
			k (int): TODO.
455
			c (int: TODO.
456
			**dparams: Additional arguments
457
458
		Returns:
459
			Tuple[numpy.ndarray, numpy.ndarray[float], Dict[str, Any]]:
460
				1. Initialized population.
461
				2. Initialized population function/fitness values.
462
				3. Additional arguments:
463
					* k (int): TODO.
464
					* c (int): TODO.
465
		"""
466
		X_gb = apply_along_axis(task.repair, 1, xb + self.FC * X[self.Rand.choice(len(X), c)] - X[self.Rand.choice(len(X), c)], self.Rand)
467
		X_gb_f = apply_along_axis(task.eval, 1, X_gb)
468
		xb, fxb = self.getBest(X_gb, X_gb_f, xb, fxb)
469
		M = full([self.NP, task.D], 1.0)
470
		for i in range(k): M[i * task.D:(i + 1) * task.D] = tril(M[i * task.D:(i + 1) * task.D])
471
		for i in range(self.NP): self.Rand.shuffle(M[i])
472
		X = apply_along_axis(task.repair, 1, M * X + vectorize(self.neg)(M) * xb, self.Rand)
473
		X_f = apply_along_axis(task.eval, 1, X)
474
		xb, fxb = self.getBest(X, X_f, xb, fxb)
475
		iw, ib_gb = argmax(X_f), argmin(X_gb_f)
476
		if X_gb_f[ib_gb] <= X_f[iw]: X[iw], X_f[iw] = X_gb[ib_gb], X_gb_f[ib_gb]
477
		return X, X_f, xb, fxb, {'k': k, 'c': c}
478
479
# vim: tabstop=3 noexpandtab shiftwidth=3 softtabstop=3
480