Passed
Pull Request — master (#202)
by
unknown
01:13
created

AgingIndividual.__init__()   A

Complexity

Conditions 1

Size

Total Lines 11
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nop 2
dl 0
loc 11
rs 10
c 0
b 0
f 0
1
# encoding=utf8
2
# pylint: disable=mixed-indentation, multiple-statements, line-too-long, unused-argument, no-self-use, no-self-use, attribute-defined-outside-init, logging-not-lazy, len-as-condition, singleton-comparison, arguments-differ, bad-continuation, dangerous-default-value, keyword-arg-before-vararg
3
import logging
4
5
from numpy import random as rand, argmin, argmax, mean, cos, asarray, append, sin
6
from scipy.spatial.distance import euclidean
7
8
from NiaPy.algorithms.algorithm import Algorithm, Individual, defaultIndividualInit
9
from NiaPy.util.utility import objects2array
10
11
__all__ = ['DifferentialEvolution', 'DynNpDifferentialEvolution', 'AgingNpDifferentialEvolution', 'CrowdingDifferentialEvolution', 'MultiStrategyDifferentialEvolution', 'DynNpMultiStrategyDifferentialEvolution', 'AgingNpMultiMutationDifferentialEvolution', 'AgingIndividual', 'CrossRand1', 'CrossBest2', 'CrossBest1', 'CrossBest2', 'CrossCurr2Rand1', 'CrossCurr2Best1', 'multiMutations']
12
13
logging.basicConfig()
14
logger = logging.getLogger('NiaPy.algorithms.basic')
15
logger.setLevel('INFO')
16
17 View Code Duplication
def CrossRand1(pop, ic, x_b, f, cr, rnd=rand, *args):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
18
	r"""Mutation strategy with crossover.
19
20
	Mutation strategy uses three different random individuals from population to perform mutation.
21
22
	Mutation:
23
		Name: DE/rand/1
24
25
		:math:`\mathbf{x}_{r_1, G} + F \cdot (\mathbf{x}_{r_2, G} - \mathbf{x}_{r_3, G}`
26
		where :math:`r_1, r_2, r_3` are random indexes representing current population individuals.
27
28
	Crossover:
29
		Name: Binomial crossover
30
31
		:math:`\mathbf{x}_{i, G+1} = \begin{cases} \mathbf{u}_{i, G+1}, & \text{if $f(\mathbf{u}_{i, G+1}) \leq f(\mathbf{x}_{i, G})$}, \\ \mathbf{x}_{i, G}, & \text{otherwise}. \end{cases}`
32
33
	Args:
34
		pop (numpy.ndarray[Individual]): Current population.
35
		ic (int): Index of individual being mutated.
36
		x_b (Individual): Current global best individual.
37
		f (float): Scale factor.
38
		cr (float): Crossover probability.
39
		rnd (mtrand.RandomState): Random generator.
40
		*args (list): Additional arguments.
41
42
	Returns:
43
		numpy.ndarray: Mutated and mixed individual.
44
	"""
45
	j = rnd.randint(len(pop[ic]))
46
	p = [1 / (len(pop) - 1.0) if i != ic else 0 for i in range(len(pop))] if len(pop) > 3 else None
47
	r = rnd.choice(len(pop), 3, replace=not len(pop) >= 3, p=p)
48
	x = [pop[r[0]][i] + f * (pop[r[1]][i] - pop[r[2]][i]) if rnd.rand() < cr or i == j else pop[ic][i] for i in range(len(pop[ic]))]
49
	return asarray(x)
50
51 View Code Duplication
def CrossBest1(pop, ic, x_b, f, cr, rnd=rand, *args):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
52
	r"""Mutation strategy with crossover.
53
54
	Mutation strategy uses two different random individuals from population and global best individual.
55
56
	Mutation:
57
		Name: de/best/1
58
59
		:math:`\mathbf{v}_{i, G} = \mathbf{x}_{best, G} + F \cdot (\mathbf{x}_{r_1, G} - \mathbf{x}_{r_2, G})`
60
		where :math:`r_1, r_2` are random indexes representing current population individuals.
61
62
	Crossover:
63
		Name: Binomial crossover
64
65
		:math:`\mathbf{x}_{i, G+1} = \begin{cases} \mathbf{u}_{i, G+1}, & \text{if $f(\mathbf{u}_{i, G+1}) \leq f(\mathbf{x}_{i, G})$}, \\ \mathbf{x}_{i, G}, & \text{otherwise}. \end{cases}`
66
67
	args:
68
		pop (numpy.ndarray[Individual]): Current population.
69
		ic (int): Index of individual being mutated.
70
		x_b (Individual): Current global best individual.
71
		f (float): Scale factor.
72
		cr (float): Crossover probability.
73
		rnd (mtrand.RandomState): Random generator.
74
		*args (list): Additional arguments.
75
76
	returns:
77
		numpy.ndarray: Mutated and mixed individual.
78
	"""
79
	j = rnd.randint(len(pop[ic]))
80
	p = [1 / (len(pop) - 1.0) if i != ic else 0 for i in range(len(pop))] if len(pop) > 2 else None
81
	r = rnd.choice(len(pop), 2, replace=not len(pop) >= 2, p=p)
82
	x = [x_b[i] + f * (pop[r[0]][i] - pop[r[1]][i]) if rnd.rand() < cr or i == j else pop[ic][i] for i in range(len(pop[ic]))]
83
	return asarray(x)
84
85 View Code Duplication
def CrossRand2(pop, ic, x_b, f, cr, rnd=rand, *args):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
86
	r"""Mutation strategy with crossover.
87
88
	Mutation strategy uses five different random individuals from population.
89
90
	Mutation:
91
		Name: de/best/1
92
93
		:math:`\mathbf{v}_{i, G} = \mathbf{x}_{r_1, G} + F \cdot (\mathbf{x}_{r_2, G} - \mathbf{x}_{r_3, G}) + F \cdot (\mathbf{x}_{r_4, G} - \mathbf{x}_{r_5, G})`
94
		where :math:`r_1, r_2, r_3, r_4, r_5` are random indexes representing current population individuals.
95
96
	Crossover:
97
		Name: Binomial crossover
98
99
		:math:`\mathbf{x}_{i, G+1} = \begin{cases} \mathbf{u}_{i, G+1}, & \text{if $f(\mathbf{u}_{i, G+1}) \leq f(\mathbf{x}_{i, G})$}, \\ \mathbf{x}_{i, G}, & \text{otherwise}. \end{cases}`
100
101
	Args:
102
		pop (numpy.ndarray[Individual]): Current population.
103
		ic (int): Index of individual being mutated.
104
		x_b (Individual): Current global best individual.
105
		f (float): Scale factor.
106
		cr (float): Crossover probability.
107
		rnd (mtrand.RandomState): Random generator.
108
		*args (list): Additional arguments.
109
110
	Returns:
111
		numpy.ndarray: mutated and mixed individual.
112
	"""
113
	j = rnd.randint(len(pop[ic]))
114
	p = [1 / (len(pop) - 1.0) if i != ic else 0 for i in range(len(pop))] if len(pop) > 5 else None
115
	r = rnd.choice(len(pop), 5, replace=not len(pop) >= 5, p=p)
116
	x = [pop[r[0]][i] + f * (pop[r[1]][i] - pop[r[2]][i]) + f * (pop[r[3]][i] - pop[r[4]][i]) if rnd.rand() < cr or i == j else pop[ic][i] for i in range(len(pop[ic]))]
117
	return asarray(x)
118
119 View Code Duplication
def CrossBest2(pop, ic, x_b, f, cr, rnd=rand, *args):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
120
	r"""Mutation strategy with crossover.
121
122
	Mutation:
123
		Name: de/best/2
124
125
		:math:`\mathbf{v}_{i, G} = \mathbf{x}_{best, G} + F \cdot (\mathbf{x}_{r_1, G} - \mathbf{x}_{r_2, G}) + F \cdot (\mathbf{x}_{r_3, G} - \mathbf{x}_{r_4, G})`
126
		where :math:`r_1, r_2, r_3, r_4` are random indexes representing current population individuals.
127
128
	Crossover:
129
		Name: Binomial crossover
130
131
		:math:`\mathbf{x}_{i, G+1} = \begin{cases} \mathbf{u}_{i, G+1}, & \text{if $f(\mathbf{u}_{i, G+1}) \leq f(\mathbf{x}_{i, G})$}, \\ \mathbf{x}_{i, G}, & \text{otherwise}. \end{cases}`
132
133
	Args:
134
		pop (numpy.ndarray[Individual]): Current population.
135
		ic (int): Index of individual being mutated.
136
		x_b (Individual): Current global best individual.
137
		f (float): Scale factor.
138
		cr (float): Crossover probability.
139
		rnd (mtrand.RandomState): Random generator.
140
		*args (list): Additional arguments.
141
142
	Returns:
143
		numpy.ndarray: mutated and mixed individual.
144
	"""
145
	j = rnd.randint(len(pop[ic]))
146
	p = [1 / (len(pop) - 1.0) if i != ic else 0 for i in range(len(pop))] if len(pop) > 4 else None
147
	r = rnd.choice(len(pop), 4, replace=not len(pop) >= 4, p=p)
148
	x = [x_b[i] + f * (pop[r[0]][i] - pop[r[1]][i]) + f * (pop[r[2]][i] - pop[r[3]][i]) if rnd.rand() < cr or i == j else pop[ic][i] for i in range(len(pop[ic]))]
149
	return asarray(x)
150
151 View Code Duplication
def CrossCurr2Rand1(pop, ic, x_b, f, cr, rnd=rand, *args):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
152
	r"""Mutation strategy with crossover.
153
154
	Mutation:
155
		Name: de/curr2rand/1
156
157
		:math:`\mathbf{v}_{i, G} = \mathbf{x}_{i, G} + F \cdot (\mathbf{x}_{r_1, G} - \mathbf{x}_{r_2, G}) + F \cdot (\mathbf{x}_{r_3, G} - \mathbf{x}_{r_4, G})`
158
		where :math:`r_1, r_2, r_3, r_4` are random indexes representing current population individuals
159
160
	Crossover:
161
		Name: Binomial crossover
162
163
		:math:`\mathbf{x}_{i, G+1} = \begin{cases} \mathbf{u}_{i, G+1}, & \text{if $f(\mathbf{u}_{i, G+1}) \leq f(\mathbf{x}_{i, G})$}, \\ \mathbf{x}_{i, G}, & \text{otherwise}. \end{cases}`
164
165
	Args:
166
		pop (numpy.ndarray[Individual]): Current population.
167
		ic (int): Index of individual being mutated.
168
		x_b (Individual): Current global best individual.
169
		f (float): Scale factor.
170
		cr (float): Crossover probability.
171
		rnd (mtrand.RandomState): Random generator.
172
		*args (list): Additional arguments.
173
174
	Returns:
175
		numpy.ndarray: mutated and mixed individual.
176
	"""
177
	j = rnd.randint(len(pop[ic]))
178
	p = [1 / (len(pop) - 1.0) if i != ic else 0 for i in range(len(pop))] if len(pop) > 4 else None
179
	r = rnd.choice(len(pop), 4, replace=not len(pop) >= 4, p=p)
180
	x = [pop[ic][i] + f * (pop[r[0]][i] - pop[r[1]][i]) + f * (pop[r[2]][i] - pop[r[3]][i]) if rnd.rand() < cr or i == j else pop[ic][i] for i in range(len(pop[ic]))]
181
	return asarray(x)
182
183
def CrossCurr2Best1(pop, ic, x_b, f, cr, rnd=rand, **kwargs):
184
	r"""Mutation strategy with crossover.
185
186
	Mutation:
187
		Name: de/curr-to-best/1
188
189
		:math:`\mathbf{v}_{i, G} = \mathbf{x}_{i, G} + F \cdot (\mathbf{x}_{r_1, G} - \mathbf{x}_{r_2, G}) + F \cdot (\mathbf{x}_{r_3, G} - \mathbf{x}_{r_4, G})`
190
		where :math:`r_1, r_2, r_3, r_4` are random indexes representing current population individuals
191
192
	Crossover:
193
		Name: Binomial crossover
194
195
		:math:`\mathbf{x}_{i, G+1} = \begin{cases} \mathbf{u}_{i, G+1}, & \text{if $f(\mathbf{u}_{i, G+1}) \leq f(\mathbf{x}_{i, G})$}, \\ \mathbf{x}_{i, G}, & \text{otherwise}. \end{cases}`
196
197
	Args:
198
		pop (numpy.ndarray[Individual]): Current population.
199
		ic (int): Index of individual being mutated.
200
		x_b (Individual): Current global best individual.
201
		f (float): Scale factor.
202
		cr (float): Crossover probability.
203
		rnd (mtrand.RandomState): Random generator.
204
		*args (list): Additional arguments.
205
206
	Returns:
207
		numpy.ndarray: mutated and mixed individual.
208
	"""
209
	j = rnd.randint(len(pop[ic]))
210
	p = [1 / (len(pop) - 1.0) if i != ic else 0 for i in range(len(pop))] if len(pop) > 3 else None
211
	r = rnd.choice(len(pop), 3, replace=not len(pop) >= 3, p=p)
212
	x = [pop[ic][i] + f * (x_b[i] - pop[r[0]][i]) + f * (pop[r[1]][i] - pop[r[2]][i]) if rnd.rand() < cr or i == j else pop[ic][i] for i in range(len(pop[ic]))]
213
	return asarray(x)
214
215
class DifferentialEvolution(Algorithm):
216
	r"""Implementation of Differential evolution algorithm.
217
218
	Algorithm:
219
	 	Differential evolution algorithm
220
221
	Date:
222
		2018
223
224
	Author:
225
		Uros Mlakar and Klemen Berkovič
226
227
	License:
228
		MIT
229
230
	Reference paper:
231
		Storn, Rainer, and Kenneth Price. "Differential evolution - a simple and efficient heuristic for global optimization over continuous spaces." Journal of global optimization 11.4 (1997): 341-359.
232
233
	Attributes:
234
		Name (List[str]): List of string of names for algorithm.
235
		F (float): Scale factor.
236
		CR (float): Crossover probability.
237
		CrossMutt (Callable[numpy.ndarray, int, numpy.ndarray, float, float, mtrand.RandomState, Dict[str, Any]]): crossover and mutation strategy.
238
239
	See Also:
240
		* :class:`NiaPy.algorithms.Algorithm`
241
	"""
242
	Name = ['DifferentialEvolution', 'DE']
243
244
	@staticmethod
245
	def typeParameters():
246
		r"""Get dictionary with functions for checking values of parameters.
247
248
		Returns:
249
			Dict[str, Callable]:
250
				* F (Callable[[Union[float, int]], bool]): Check for correct value of parameter.
251
				* CR (Callable[[float], bool]): Check for correct value of parameter.
252
253
		See Also:
254
			* :func:`NiaPy.algorithms.Algorithm.typeParameters`
255
		"""
256
		d = Algorithm.typeParameters()
257
		d.update({
258
			'F': lambda x: isinstance(x, (float, int)) and 0 < x <= 2,
259
			'CR': lambda x: isinstance(x, float) and 0 <= x <= 1
260
		})
261
		return d
262
263
	def setParameters(self, NP=50, F=1, CR=0.8, CrossMutt=CrossRand1, **ukwargs):
264
		r"""Set the algorithm parameters.
265
266
		Arguments:
267
			NP (Optional[int]): Population size.
268
			F (Optional[float]): Scaling factor.
269
			CR (Optional[float]): Crossover rate.
270
			CrossMutt (Optional[Callable[[numpy.ndarray, int, numpy.ndarray, float, float, mtrand.RandomState, list], numpy.ndarray]]): Crossover and mutation strategy.
271
			ukwargs (Dict[str, Any]): Additional arguments.
272
273
		See Also:
274
			* :func:`NiaPy.algorithms.Algorithm.setParameters`
275
		"""
276
		Algorithm.setParameters(self, NP=NP, InitPopFunc=ukwargs.pop('InitPopFunc', defaultIndividualInit), itype=ukwargs.pop('itype', Individual), **ukwargs)
277
		self.F, self.CR, self.CrossMutt = F, CR, CrossMutt
278
		if ukwargs: logger.info('Unused arguments: %s' % (ukwargs))
279
280
	def evolve(self, pop, xb, task, **kwargs):
281
		r"""Evolve population.
282
283
		Args:
284
			pop (numpy.ndarray[Individual]): Current population.
285
			xb (Individual): Current best individual.
286
			task (Task): Optimization task.
287
			**kwargs (Dict[str, Any]): Additional arguments.
288
289
		Returns:
290
			numpy.ndarray[Individual]: New evolved populations.
291
		"""
292
		return objects2array([self.itype(x=self.CrossMutt(pop, i, xb, self.F, self.CR, self.Rand), task=task, rnd=self.Rand, e=True) for i in range(len(pop))])
293
294
	def selection(self, pop, npop, **kwargs):
295
		r"""Operator for selection.
296
297
		Args:
298
			pop (numpy.ndarray[Individual]): Current population.
299
			npop (numpy.ndarray[Individual]): New Population.
300
			**kwargs (Dict[str, Any]): Additional arguments.
301
302
		Returns:
303
			numpy.ndarray[Individual]: New selected individuals.
304
		"""
305
		return objects2array([e if e.f < pop[i].f else pop[i] for i, e in enumerate(npop)])
306
307
	def postSelection(self, pop, task, xb=None, **kwargs):
308
		r"""Apply additional operation after selection.
309
310
		Args:
311
			pop (numpy.ndarray[Individual]): Current population.
312
			task (Task): Optimization task.
313
			xb (Optional[Individual]): Global best solution.
314
			**kwargs (Dict[str, Any]): Additional arguments.
315
316
		Returns:
317
			numpy.ndarray[Individual]: New population.
318
		"""
319
		return pop
320
321
	def runIteration(self, task, pop, fpop, xb, fxb, **dparams):
322
		r"""Core function of Differential Evolution algorithm.
323
324
		Args:
325
			task (Task): Optimization task.
326
			pop (numpy.ndarray[Initialized]): Current population.
327
			fpop (numpy.ndarray[float]): Current populations fitness/function values.
328
			xb (Individual): Current best individual.
329
			fxb (float): Current best individual function/fitness value.
330
			**dparams (Dict[str, Any]): Additional arguments.
331
332
		Returns:
333
			Tuple[numpy.ndarray[Individual], numpy.ndarray[float], Dict[str, Any]]:
334
				1. New population.
335
				2. New population fitness/function values.
336
				3. Additional arguments.
337
338
		See Also:
339
			* :func:`NiaPy.algorithms.basic.DifferentialEvolution.evolve`
340
			* :func:`NiaPy.algorithms.basic.DifferentialEvolution.selection`
341
			* :func:`NiaPy.algorithms.basic.DifferentialEvolution.postSelection`
342
		"""
343
		npop = self.evolve(pop, xb, task)
344
		pop = self.selection(pop, npop, task=task)
345
		pop = self.postSelection(pop, task, xb=xb)
346
		return pop, asarray([x.f for x in pop]), {}
347
348
class CrowdingDifferentialEvolution(DifferentialEvolution):
349
	r"""Implementation of Differential evolution algorithm with multiple mutation strateys.
350
351
	Algorithm:
352
		Implementation of Differential evolution algorithm with multiple mutation strateys
353
354
	Date:
355
		2018
356
357
	Author:
358
		Klemen Berkovič
359
360
	License:
361
		MIT
362
363
	Attributes:
364
		Name (List[str]): List of strings representing algorithm name.
365
		CrowPop (float): Proportion of range for cowding.
366
367
	See Also:
368
		* :class:`NiaPy.algorithms.basic.DifferentialEvolution`
369
	"""
370
	Name = ['CrowdingDifferentialEvolution', 'CDE']
371
372
	def __init__(self, **kwargs):
373
		r"""Init CrowdingDifferentialEvolution algorithm.
374
375
		Args:
376
			**kwargs (Dict[str, Any]): Additional arguments.
377
378
		See Also:
379
			* :func:`NiaPy.algorithms.basic.DifferentialEvolution.__init__`
380
		"""
381
		DifferentialEvolution.__init__(self, **kwargs)
382
383
	def setParameters(self, CrowPop=0.1, **ukwargs):
384
		r"""Set core parameters of algorithm.
385
386
		Args:
387
			CrowPop (Optional[float]): Crowding distance.
388
			**ukwargs: Additional arguments.
389
390
		See Also:
391
			* :func:`NiaPy.algorithms.basic.DifferentialEvolution.setParameters`
392
		"""
393
		DifferentialEvolution.setParameters(self, **ukwargs)
394
		self.CrowPop = CrowPop
395
396
	def selection(self, pop, npop, **kwargs):
397
		r"""Operator for selection of individuals.
398
399
		Args:
400
			pop (numpy.ndarray[Individual]): Current population.
401
			npop (numpy.ndarray[Individual]): New population.
402
			kwargs (Dict[str, Any]): Additional arguments.
403
404
		Returns:
405
			numpy.ndarray[Individual]: New population.
406
		"""
407
		P = []
408
		for e in npop:
409
			i = argmin([euclidean(e, f) for f in pop])
410
			P.append(pop[i] if pop[i].f < e.f else e)
411
		return asarray(P)
412
413 View Code Duplication
class DynNpDifferentialEvolution(DifferentialEvolution):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
414
	r"""Implementation of Dynamic poulation size Differential evolution algorithm.
415
416
	Algorithm:
417
		Dynamic poulation size Differential evolution algorithm
418
419
	Date:
420
		2018
421
422
	Author:
423
		Klemen Berkovič
424
425
	License:
426
		MIT
427
428
	Attributes:
429
		Name (List[str]): List of strings representing algorithm names.
430
		pmax (int): TODO
431
		rp (int): TODO
432
433
	See Also:
434
		* :class:`NiaPy.algorithms.basic.DifferentialEvolution`
435
	"""
436
	Name = ['DynNpDifferentialEvolution', 'dynNpDE']
437
438
	@staticmethod
439
	def typeParameters():
440
		r"""Get dictionary with functions for checking values of parameters.
441
442
		Returns:
443
			Dict[str, Callable]:
444
				* rp (Callable[[Union[float, int]], bool]): TODO
445
				* pmax (Callable[[int], bool]): TODO
446
447
		See Also:
448
			* :func:`NiaPy.algorithms.basic.DifferentialEvolution.typeParameters`
449
		"""
450
		r = DifferentialEvolution.typeParameters()
451
		r['rp'] = lambda x: isinstance(x, (float, int)) and x > 0
452
		r['pmax'] = lambda x: isinstance(x, int) and x > 0
453
		return r
454
455
	def setParameters(self, pmax=50, rp=3, **ukwargs):
456
		r"""Set the algorithm parameters.
457
458
		Arguments:
459
			pmax (Optional[int]): TODO
460
			rp (Optional[int]): TODO
461
462
		See Also:
463
			* :func:`NiaPy.algorithms.basic.DifferentialEvolution.setParameters`
464
		"""
465
		DifferentialEvolution.setParameters(self, **ukwargs)
466
		self.pmax, self.rp = pmax, rp
467
		if ukwargs: logger.info('Unused arguments: %s' % (ukwargs))
468
469
	def postSelection(self, pop, task, **kwargs):
470
		r"""Post selection operator.
471
472
		In this algorithm the post selection operator decrements the population at specific iterations/generations.
473
474
		Args:
475
			pop (numpy.ndarray[Individual]): Current population.
476
			task (Task): Optimization task.
477
			kwargs (Dict[str, Any]): Additional arguments.
478
479
		Returns:
480
			numpy.ndarray[Individual]: Changed current population.
481
		"""
482
		Gr = task.nFES // (self.pmax * len(pop)) + self.rp
483
		nNP = len(pop) // 2
484
		if task.Iters == Gr and len(pop) > 3: pop = objects2array([pop[i] if pop[i].f < pop[i + nNP].f else pop[i + nNP] for i in range(nNP)])
485
		return pop
486
487
def proportional(Lt_min, Lt_max, mu, x_f, avg, *args):
488
	r"""Proportional calculation of age of individual.
489
490
	Args:
491
		Lt_min (int): Minimal life time.
492
		Lt_max (int): Maximal life time.
493
		mu (float): Median of life time.
494
		x_f (float): Individuals function/fitness value.
495
		avg (float): Average fitness/function value of current population.
496
		*args (list): Additional arguments.
497
498
	Returns:
499
		int: Age of individual.
500
	"""
501
	return min(Lt_min + mu * avg / x_f, Lt_max)
502
503
def linear(Lt_min, Lt_max, mu, x_f, avg, x_gw, x_gb, *args):
504
	r"""Linear calculation of age of individual.
505
506
	Args:
507
		Lt_min (int): Minimal life time.
508
		Lt_max (int): Maximal life time.
509
		mu (float): Median of life time.
510
		x_f (float): Individual function/fitness value.
511
		avg (float): Average fitness/function value.
512
		x_gw (float): Global worst fitness/function value.
513
		x_gb (float): Global best fitness/function value.
514
		*args (list): Additional arguments.
515
516
	Returns:
517
		int: Age of individual.
518
	"""
519
	return Lt_min + 2 * mu * (x_f - x_gw) / (x_gb - x_gw)
520
521
def bilinear(Lt_min, Lt_max, mu, x_f, avg, x_gw, x_gb, *args):
522
	r"""Bilinear calculation of age of individual.
523
524
	Args:
525
		Lt_min (int): Minimal life time.
526
		Lt_max (int): Maximal life time.
527
		mu (float): Median of life time.
528
		x_f (float): Individual function/fitness value.
529
		avg (float): Average fitness/function value.
530
		x_gw (float): Global worst fitness/function value.
531
		x_gb (float): Global best fitness/function value.
532
		*args (list): Additional arguments.
533
534
	Returns:
535
		int: Age of individual.
536
	"""
537
	if avg < x_f: return Lt_min + mu * (x_f - x_gw) / (x_gb - x_gw)
538
	return 0.5 * (Lt_min + Lt_max) + mu * (x_f - avg) / (x_gb - avg)
539
540
class AgingIndividual(Individual):
541
	r"""Individual with aging.
542
543
	Attributes:
544
		age (int): Age of individual.
545
546
	See Also:
547
		* :class:`NiaPy.algorithms.Individual`
548
	"""
549
	age = 0
550
551
	def __init__(self, **kwargs):
552
		r"""Init Aging Individual.
553
554
		Args:
555
			**kwargs (Dict[str, Any]): Additional arguments sent to parent.
556
557
		See Also:
558
			* :func:`NiaPy.algorithms.Individual.__init__`
559
		"""
560
		Individual.__init__(self, **kwargs)
561
		self.age = 0
562
563
class AgingNpDifferentialEvolution(DifferentialEvolution):
564
	r"""Implementation of Differential evolution algorithm with aging individuals.
565
566
	Algorithm:
567
		Differential evolution algorithm with dynamic population size that is defined by the quality of population
568
569
	Date:
570
		2018
571
572
	Author:
573
		Klemen Berkovič
574
575
	License:
576
		MIT
577
578
	Attributes:
579
		Name (List[str]): list of strings representing algorithm names.
580
		Lt_min (int): Minimal age of individual.
581
		Lt_max (int): Maximal age of individual.
582
		delta_np (float): Proportion of how many individuals shall die.
583
		omega (float): Acceptance rate for individuals to die.
584
		mu (int): Mean of individual max and min age.
585
		age (Callable[[int, int, float, float, float, float, float], int]): Function for calculation of age for individual.
586
587
	See Also:
588
		* :class:`NiaPy.algorithms.basic.DifferentialEvolution`
589
	"""
590
	Name = ['AgingNpDifferentialEvolution', 'ANpDE']
591
592
	@staticmethod
593
	def typeParameters():
594
		r"""Get dictionary with functions for checking values of parameters.
595
596
		Returns:
597
			Dict[str, Callable]:
598
				* Lt_min (Callable[[int], bool])
599
				* Lt_max (Callable[[int], bool])
600
				* delta_np (Callable[[float], bool])
601
				* omega (Callable[[float], bool])
602
603
		See Also:
604
			* :func:`NiaPy.algorithms.basic.DifferentialEvolution.typeParameters`
605
		"""
606
		r = DifferentialEvolution.typeParameters()
607
		r.update({
608
			'Lt_min': lambda x: isinstance(x, int) and x >= 0,
609
			'Lt_max': lambda x: isinstance(x, int) and x >= 0,
610
			'delta_np': lambda x: isinstance(x, float) and 0 <= x <= 1,
611
			'omega': lambda x: isinstance(x, float) and 1 >= x >= 0
612
		})
613
		return r
614
615
	def setParameters(self, Lt_min=0, Lt_max=12, delta_np=0.3, omega=0.3, age=proportional, CrossMutt=CrossBest1, **ukwargs):
616
		r"""Set the algorithm parameters.
617
618
		Arguments:
619
			Lt_min (Optional[int]): Minimum life time.
620
			Lt_max (Optional[int]): Maximum life time.
621
			age (Optional[Callable[[int, int, float, float, float, float, float], int]]): Function for calculation of age for individual.
622
623
		See Also:
624
			* :func:`NiaPy.algorithms.basic.DifferentialEvolution.setParameters`
625
		"""
626
		DifferentialEvolution.setParameters(self, itype=AgingIndividual, **ukwargs)
627
		self.Lt_min, self.Lt_max, self.age, self.delta_np, self.omega = Lt_min, Lt_max, age, delta_np, omega
628
		self.mu = abs(self.Lt_max - self.Lt_min) / 2
629
		if ukwargs: logger.info('Unused arguments: %s' % (ukwargs))
630
631
	def deltaPopE(self, t):
632
		r"""Calculate how many individuals are going to dye.
633
634
		Args:
635
			t (int): Number of generations made by the algorithm.
636
637
		Returns:
638
			int: Number of individuals to dye.
639
		"""
640
		return int(self.delta_np * abs(sin(t)))
641
642
	def deltaPopC(self, t):
643
		r"""Calculate how many individuals are going to be created.
644
645
		Args:
646
			t (int): Number of generations made by the algorithm.
647
648
		Returns:
649
			int: Number of individuals to be born.
650
		"""
651
		return int(self.delta_np * abs(cos(t)))
652
653
	def aging(self, task, pop):
654
		r"""Apply aging to individuals.
655
656
		Args:
657
			task (Task): Optimization task.
658
			pop (numpy.ndarray[Individual]): Current population.
659
660
		Returns:
661
			numpy.ndarray[Individual]: New population.
662
		"""
663
		fpop = asarray([x.f for x in pop])
664
		x_b, x_w = pop[argmin(fpop)], pop[argmax(fpop)]
665
		avg, npop = mean(fpop), []
666
		for x in pop:
667
			x.age += 1
668
			Lt = round(self.age(self.Lt_min, self.Lt_max, self.mu, x.f, avg, x_w, x_b))
669
			if x.age <= Lt: npop.append(x)
670
		if len(npop) != 0: npop = objects2array([self.itype(task=task, rnd=self.Rand, e=True) for _i in range(len(pop))])
671
		return npop
672
673
	def popIncrement(self, pop, task):
674
		r"""Increment population.
675
676
		Args:
677
			pop (numpy.ndarray[Individual]): Current population.
678
			task (Task): Optimization task.
679
680
		Returns:
681
			numpy.ndarray[Individual]: Increased population.
682
		"""
683
		deltapop = int(round(max(1, self.NP * self.deltaPopE(task.Iters))))
684
		return objects2array([self.itype(task=task, rnd=self.Rand, e=True) for _ in range(deltapop)])
685
686
	def popDecrement(self, pop, task):
687
		r"""Decrement population.
688
689
		Args:
690
			pop (numpy.ndarray): Current population.
691
			task (Task): Optimization task.
692
693
		Returns:
694
			numpy.ndarray[Individual]: Decreased population.
695
		"""
696
		deltapop = int(round(max(1, self.NP * self.deltaPopC(task.Iters))))
697
		if len(pop) - deltapop <= 0: return pop
698
		ni = self.Rand.choice(len(pop), deltapop, replace=False)
699
		npop = []
700
		for i, e in enumerate(pop):
701
			if i not in ni: npop.append(e)
702
			elif self.rand() >= self.omega: npop.append(e)
703
		return objects2array(npop)
704
705
	def selection(self, pop, npop, task, **kwargs):
706
		r"""Modified selection operator with aging.
707
708
		Args:
709
			pop (numpy.ndarray[Individual]): Current population.
710
			npop (numpy.ndarray[Individual]): New population.
711
			task (Task): Optimization task.
712
			**kwargs (Dict[str, Any]): Additional arguments.
713
714
		Returns:
715
			numpy.ndarray[Individual]: New population of individuals.
716
		"""
717
		npop = DifferentialEvolution.selection(self, pop, npop)
718
		npop = append(npop, self.popIncrement(pop, task))
719
		pop = self.aging(task, npop)
720
		return pop
721
722
	def postSelection(self, pop, task, xb=None, **kwargs):
723
		r"""Post selection operator.
724
725
		Args:
726
			pop (numpy.ndarray): Current population.
727
			task (Task): Optimization task.
728
			xb (Individual): Global best individual.
729
			**kwargs (Dict[str, Any]): Additional arguments.
730
731
		Returns:
732
			numpy.ndarray[Individual]: New population.
733
		"""
734
		return self.popDecrement(pop, task) if len(pop) > self.NP else pop
735
736
def multiMutations(pop, i, xb, F, CR, rnd, task, itype, strategies, **kwargs):
737
	r"""Mutation strategy that takes more than one strategy and applys them to individual.
738
739
	Args:
740
		pop (numpy.ndarray[Individual]): Current population.
741
		i (int): Index of current individual.
742
		xb (Individual): Current best individual.
743
		F (float): Scale factor.
744
		CR (float): Crossover probability.
745
		rnd (mtrand.RandomState): Random generator.
746
		task (Task): Optimization task.
747
		IndividualType (Individual): Individual type used in algorithm.
748
		strategies (Iterable[Callable[[numpy.ndarray[Individual], int, Individual, float, float, mtrand.RandomState], numpy.ndarray[Individual]]]): List of mutation strategies.
749
		**kwargs (Dict[str, Any]): Additional arguments.
750
751
	Returns:
752
		Individual: Best individual from applyed mutations strategies.
753
	"""
754
	L = [itype(x=strategy(pop, i, xb, F, CR, rnd=rnd), task=task, e=True, rnd=rnd) for strategy in strategies]
755
	return L[argmin([x.f for x in L])]
756
757
class MultiStrategyDifferentialEvolution(DifferentialEvolution):
758
	r"""Implementation of Differential evolution algorithm with multiple mutation strateys.
759
760
	Algorithm:
761
		Implementation of Differential evolution algorithm with multiple mutation strateys
762
763
	Date:
764
		2018
765
766
	Author:
767
		Klemen Berkovič
768
769
	License:
770
		MIT
771
772
	Attributes:
773
		Name (List[str]): List of strings representing algorithm names.
774
		strategies (Iterable[Callable[[numpy.ndarray[Individual], int, Individual, float, float, mtrand.RandomState], numpy.ndarray[Individual]]]): List of mutation strategies.
775
		CrossMutt (Callable[[numpy.ndarray[Individual], int, Individual, float, float, Task, Individual, Iterable[Callable[[numpy.ndarray, int, numpy.ndarray, float, float, mtrand.RandomState, Dict[str, Any]], Individual]]], Individual]): Multi crossover and mutation combiner function.
776
777
	See Also:
778
		* :class:`NiaPy.algorithms.basic.DifferentialEvolution`
779
	"""
780
	Name = ['MultiStrategyDifferentialEvolution', 'MsDE']
781
782
	@staticmethod
783
	def typeParameters():
784
		r"""Get dictionary with functions for checking values of parameters.
785
786
		Returns:
787
			Dict[str, Callable]:
788
				* CrossMutt (Callable[[Callable, bool])
789
790
		See Also:
791
			* :func:`NiaPy.algorithms.basic.DifferentialEvolution.typeParameters`
792
		"""
793
		r = DifferentialEvolution.typeParameters()
794
		r.pop('CrossMutt', None)
795
		# TODO add constraint method for selection of stratgy methos
796
		return r
797
798
	def setParameters(self, strategies=(CrossRand1, CrossBest1, CrossCurr2Best1, CrossRand2), **ukwargs):
799
		r"""Set the arguments of the algorithm.
800
801
		Arguments:
802
			strategies (Optional[Iterable[Callable[[numpy.ndarray[Individual], int, Individual, float, float, mtrand.RandomState], numpy.ndarray[Individual]]]]): List of mutation strategyis.
803
			CrossMutt (Optional[Callable[[numpy.ndarray[Individual], int, Individual, float, float, Task, Individual, Iterable[Callable[[numpy.ndarray, int, numpy.ndarray, float, float, mtrand.RandomState, Dict[str, Any]], Individual]]], Individual]]): Multi crossover and mutation combiner function.
804
805
		See Also:
806
			* :func:`NiaPy.algorithms.basic.DifferentialEvolution.setParameters`
807
		"""
808
		DifferentialEvolution.setParameters(self, CrossMutt=multiMutations, **ukwargs)
809
		self.strategies = strategies
810
811 View Code Duplication
	def evolve(self, pop, xb, task, **kwargs):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
812
		r"""Evolve population with the help multiple mutation strategies.
813
814
		Args:
815
			pop (numpy.ndarray[Individual]): Current population.
816
			xb (Individual): Current best individual.
817
			task (Task): Optimization task.
818
			**kwargs (Dict[str, Any]): Additional arguments.
819
820
		Returns:
821
			numpy.ndarray[Individual]: New population of individuals.
822
		"""
823
		return objects2array([self.CrossMutt(pop, i, xb, self.F, self.CR, self.Rand, task, self.itype, self.strategies) for i in range(len(pop))])
824
825
class DynNpMultiStrategyDifferentialEvolution(MultiStrategyDifferentialEvolution, DynNpDifferentialEvolution):
826
	r"""Implementation of Dynamic population size Differential evolution algorithm with dynamic population size that is defined by the quality of population.
827
828
	Algorithm:
829
		Dynamic population size Differential evolution algorithm with dynamic population size that is defined by the quality of population
830
831
	Date:
832
		2018
833
834
	Author:
835
		Klemen Berkovič
836
837
	License:
838
		MIT
839
840
	Attributes:
841
		Name (List[str]): List of strings representing algorithm name.
842
843
	See Also:
844
		* :class:`NiaPy.algorithms.basic.MultiStrategyDifferentialEvolution`
845
		* :class:`NiaPy.algorithms.basic.DynNpDifferentialEvolution`
846
	"""
847
	Name = ['DynNpMultiStrategyDifferentialEvolution', 'dynNpMsDE']
848
849
	@staticmethod
850
	def typeParameters():
851
		r"""Get dictionary with functions for checking values of parameters.
852
853
		Returns:
854
			Dict[str, Callable]:
855
				* rp (Callable[[Union[float, int]], bool]): TODO
856
				* pmax (Callable[[int], bool]): TODO
857
858
		See Also:
859
			* :func:`NiaPy.algorithms.basic.MultiStrategyDifferentialEvolution.typeParameters`
860
		"""
861
		r = MultiStrategyDifferentialEvolution.typeParameters()
862
		r['rp'] = lambda x: isinstance(x, (float, int)) and x > 0
863
		r['pmax'] = lambda x: isinstance(x, int) and x > 0
864
		return r
865
866
	def setParameters(self, **ukwargs):
867
		r"""Set the arguments of the algorithm.
868
869
		Args:
870
			ukwargs (Dict[str, Any]): Additional arguments.
871
872
		See Also:
873
			* :func:`NiaPy.algorithms.basic.MultiStrategyDifferentialEvolution.setParameters`
874
			* :func:`NiaPy.algorithms.basic.DynNpDifferentialEvolution.setParameters`
875
		"""
876
		DynNpDifferentialEvolution.setParameters(self, **ukwargs)
877
		MultiStrategyDifferentialEvolution.setParameters(self, **ukwargs)
878
879
	def evolve(self, pop, xb, task, **kwargs):
880
		return MultiStrategyDifferentialEvolution.evolve(self, pop, xb, task, **kwargs)
881
882
	def postSelection(self, pop, task, **kwargs):
883
		r"""Post selection operator.
884
885
		Args:
886
			pop (numpy.ndarray[Individual]): Current population.
887
			task (Task): Optimization task.
888
			**kwargs (Dict[str, Any]): Additional arguments.
889
890
		Returns:
891
			numpy.ndarray: New population.
892
893
		See Also:
894
			* :func:`NiaPy.algorithms.basic.DynNpDifferentialEvolution.postSelection`
895
		"""
896
		return DynNpDifferentialEvolution.postSelection(self, pop, task)
897
898
class AgingNpMultiMutationDifferentialEvolution(AgingNpDifferentialEvolution, MultiStrategyDifferentialEvolution):
899
	r"""Implementation of Differential evolution algorithm with aging individuals.
900
901
	Algorithm:
902
		Differential evolution algorithm with dynamic population size that is defined by the quality of population
903
904
	Date:
905
		2018
906
907
	Author:
908
		Klemen Berkovič
909
910
	License:
911
		MIT
912
913
	Attributes:
914
		Name (List[str]): List of strings representing algorithm names
915
916
	See Also:
917
		* :class:`NiaPy.algorithms.basic.AgingNpDifferentialEvolution`
918
		* :class:`NiaPy.algorithms.basic.MultiStrategyDifferentialEvolution`
919
	"""
920
	Name = ['AgingNpMultiMutationDifferentialEvolution', 'ANpMSDE']
921
922
	@staticmethod
923
	def typeParameters():
924
		r"""Get dictionary with functions for checking values of parameters.
925
926
		Returns:
927
			Dict[str, Callable]:
928
				* rp (Callable[[Union[float, int]], bool]): TODO
929
				* pmax (Callable[[int], bool]): TODO
930
931
		See Also:
932
			* :func:`NiaPy.algorithms.basic.MultiStrategyDifferentialEvolution.typeParameters`
933
		"""
934
		r = AgingNpDifferentialEvolution.typeParameters()
935
		# TODO add other parameters to data check list
936
		return r
937
938
	def setParameters(self, **ukwargs):
939
		r"""Set core parameter arguments.
940
941
		Args:
942
			**ukwargs (Dict[str, Any]): Additional arguments.
943
944
		See Also:
945
			* :func:`NiaPy.algorithms.basic.AgingNpDifferentialEvolution.setParameters`
946
			* :func:`NiaPy.algorithms.basic.MultiStrategyDifferentialEvolution.setParameters`
947
		"""
948
		AgingNpDifferentialEvolution.setParameters(self, **ukwargs)
949
		MultiStrategyDifferentialEvolution.setParameters(self, stratgeys=(CrossRand1, CrossBest1, CrossCurr2Rand1, CrossRand2), itype=AgingIndividual, **ukwargs)
950
951
	def evolve(self, pop, xb, task, **kwargs):
952
		r"""Evolve current population.
953
954
		Args:
955
			pop (numpy.ndarray[Individual]): Current population.
956
			xb (Individual): Global best individual.
957
			task (Task): Optimization task.
958
			**kwargs (Dict[str, Any]): Additional arguments.
959
960
		Returns:
961
			numpy.ndarray[Individual]: New population of individuals.
962
		"""
963
		return MultiStrategyDifferentialEvolution.evolve(self, pop, xb, task, **kwargs)
964
965
# vim: tabstop=3 noexpandtab shiftwidth=3 softtabstop=3
966