Passed
Pull Request — master (#202)
by Grega
01:02
created

DifferentialEvolution.algorithmInfo()   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 0
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 algorithmInfo():
246
		r"""Get basic information of algorithm.
247
248
		Returns:
249
			str: Basic information of algorithm.
250
251
		See Also:
252
			* :func:`NiaPy.algorithms.Algorithm.algorithmInfo`
253
		"""
254
		return r"""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."""
255
256
	@staticmethod
257
	def typeParameters():
258
		r"""Get dictionary with functions for checking values of parameters.
259
260
		Returns:
261
			Dict[str, Callable]:
262
				* F (Callable[[Union[float, int]], bool]): Check for correct value of parameter.
263
				* CR (Callable[[float], bool]): Check for correct value of parameter.
264
265
		See Also:
266
			* :func:`NiaPy.algorithms.Algorithm.typeParameters`
267
		"""
268
		d = Algorithm.typeParameters()
269
		d.update({
270
			'F': lambda x: isinstance(x, (float, int)) and 0 < x <= 2,
271
			'CR': lambda x: isinstance(x, float) and 0 <= x <= 1
272
		})
273
		return d
274
275
	def setParameters(self, NP=50, F=1, CR=0.8, CrossMutt=CrossRand1, **ukwargs):
276
		r"""Set the algorithm parameters.
277
278
		Arguments:
279
			NP (Optional[int]): Population size.
280
			F (Optional[float]): Scaling factor.
281
			CR (Optional[float]): Crossover rate.
282
			CrossMutt (Optional[Callable[[numpy.ndarray, int, numpy.ndarray, float, float, mtrand.RandomState, list], numpy.ndarray]]): Crossover and mutation strategy.
283
			ukwargs (Dict[str, Any]): Additional arguments.
284
285
		See Also:
286
			* :func:`NiaPy.algorithms.Algorithm.setParameters`
287
		"""
288
		Algorithm.setParameters(self, NP=NP, InitPopFunc=ukwargs.pop('InitPopFunc', defaultIndividualInit), itype=ukwargs.pop('itype', Individual), **ukwargs)
289
		self.F, self.CR, self.CrossMutt = F, CR, CrossMutt
290
		if ukwargs: logger.info('Unused arguments: %s' % (ukwargs))
291
292
	def evolve(self, pop, xb, task, **kwargs):
293
		r"""Evolve population.
294
295
		Args:
296
			pop (numpy.ndarray[Individual]): Current population.
297
			xb (Individual): Current best individual.
298
			task (Task): Optimization task.
299
			**kwargs (Dict[str, Any]): Additional arguments.
300
301
		Returns:
302
			numpy.ndarray[Individual]: New evolved populations.
303
		"""
304
		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))])
305
306
	def selection(self, pop, npop, **kwargs):
307
		r"""Operator for selection.
308
309
		Args:
310
			pop (numpy.ndarray[Individual]): Current population.
311
			npop (numpy.ndarray[Individual]): New Population.
312
			**kwargs (Dict[str, Any]): Additional arguments.
313
314
		Returns:
315
			numpy.ndarray[Individual]: New selected individuals.
316
		"""
317
		return objects2array([e if e.f < pop[i].f else pop[i] for i, e in enumerate(npop)])
318
319
	def postSelection(self, pop, task, xb=None, **kwargs):
320
		r"""Apply additional operation after selection.
321
322
		Args:
323
			pop (numpy.ndarray[Individual]): Current population.
324
			task (Task): Optimization task.
325
			xb (Optional[Individual]): Global best solution.
326
			**kwargs (Dict[str, Any]): Additional arguments.
327
328
		Returns:
329
			numpy.ndarray[Individual]: New population.
330
		"""
331
		return pop
332
333
	def runIteration(self, task, pop, fpop, xb, fxb, **dparams):
334
		r"""Core function of Differential Evolution algorithm.
335
336
		Args:
337
			task (Task): Optimization task.
338
			pop (numpy.ndarray[Initialized]): Current population.
339
			fpop (numpy.ndarray[float]): Current populations fitness/function values.
340
			xb (Individual): Current best individual.
341
			fxb (float): Current best individual function/fitness value.
342
			**dparams (Dict[str, Any]): Additional arguments.
343
344
		Returns:
345
			Tuple[numpy.ndarray[Individual], numpy.ndarray[float], Dict[str, Any]]:
346
				1. New population.
347
				2. New population fitness/function values.
348
				3. Additional arguments.
349
350
		See Also:
351
			* :func:`NiaPy.algorithms.basic.DifferentialEvolution.evolve`
352
			* :func:`NiaPy.algorithms.basic.DifferentialEvolution.selection`
353
			* :func:`NiaPy.algorithms.basic.DifferentialEvolution.postSelection`
354
		"""
355
		npop = self.evolve(pop, xb, task)
356
		pop = self.selection(pop, npop, task=task)
357
		pop = self.postSelection(pop, task, xb=xb)
358
		return pop, asarray([x.f for x in pop]), {}
359
360
class CrowdingDifferentialEvolution(DifferentialEvolution):
361
	r"""Implementation of Differential evolution algorithm with multiple mutation strateys.
362
363
	Algorithm:
364
		Implementation of Differential evolution algorithm with multiple mutation strateys
365
366
	Date:
367
		2018
368
369
	Author:
370
		Klemen Berkovič
371
372
	License:
373
		MIT
374
375
	Attributes:
376
		Name (List[str]): List of strings representing algorithm name.
377
		CrowPop (float): Proportion of range for cowding.
378
379
	See Also:
380
		* :class:`NiaPy.algorithms.basic.DifferentialEvolution`
381
	"""
382
	Name = ['CrowdingDifferentialEvolution', 'CDE']
383
384
	@staticmethod
385
	def algorithmInfo():
386
		r"""Get basic information of algorithm.
387
388
		Returns:
389
			str: Basic information of algorithm.
390
391
		See Also:
392
			* :func:`NiaPy.algorithms.Algorithm.algorithmInfo`
393
		"""
394
		return r"""No New"""
395
396
	def setParameters(self, CrowPop=0.1, **ukwargs):
397
		r"""Set core parameters of algorithm.
398
399
		Args:
400
			CrowPop (Optional[float]): Crowding distance.
401
			**ukwargs: Additional arguments.
402
403
		See Also:
404
			* :func:`NiaPy.algorithms.basic.DifferentialEvolution.setParameters`
405
		"""
406
		DifferentialEvolution.setParameters(self, **ukwargs)
407
		self.CrowPop = CrowPop
408
409
	def selection(self, pop, npop, **kwargs):
410
		r"""Operator for selection of individuals.
411
412
		Args:
413
			pop (numpy.ndarray[Individual]): Current population.
414
			npop (numpy.ndarray[Individual]): New population.
415
			kwargs (Dict[str, Any]): Additional arguments.
416
417
		Returns:
418
			numpy.ndarray[Individual]: New population.
419
		"""
420
		P = []
421
		for e in npop:
422
			i = argmin([euclidean(e, f) for f in pop])
423
			P.append(pop[i] if pop[i].f < e.f else e)
424
		return asarray(P)
425
426
class DynNpDifferentialEvolution(DifferentialEvolution):
427
	r"""Implementation of Dynamic poulation size Differential evolution algorithm.
428
429
	Algorithm:
430
		Dynamic poulation size Differential evolution algorithm
431
432
	Date:
433
		2018
434
435
	Author:
436
		Klemen Berkovič
437
438
	License:
439
		MIT
440
441
	Attributes:
442
		Name (List[str]): List of strings representing algorithm names.
443
		pmax (int): Number of population reductions.
444
		rp (int): Small non-negative number which is added to value of generations.
445
446
	See Also:
447
		* :class:`NiaPy.algorithms.basic.DifferentialEvolution`
448
	"""
449
	Name = ['DynNpDifferentialEvolution', 'dynNpDE']
450
451
	@staticmethod
452
	def algorithmInfo():
453
		r"""Get basic information of algorithm.
454
455
		Returns:
456
			str: Basic information of algorithm.
457
458
		See Also:
459
			* :func:`NiaPy.algorithms.Algorithm.algorithmInfo`
460
		"""
461
		return r"""No info"""
462
463
	@staticmethod
464
	def typeParameters():
465
		r"""Get dictionary with functions for checking values of parameters.
466
467
		Returns:
468
			Dict[str, Callable]:
469
				* rp (Callable[[Union[float, int]], bool])
470
				* pmax (Callable[[int], bool])
471
472
		See Also:
473
			* :func:`NiaPy.algorithms.basic.DifferentialEvolution.typeParameters`
474
		"""
475
		r = DifferentialEvolution.typeParameters()
476
		r['rp'] = lambda x: isinstance(x, (float, int)) and x > 0
477
		r['pmax'] = lambda x: isinstance(x, int) and x > 0
478
		return r
479
480
	def setParameters(self, pmax=50, rp=3, **ukwargs):
481
		r"""Set the algorithm parameters.
482
483
		Arguments:
484
			pmax (Optional[int]): umber of population reductions.
485
			rp (Optional[int]): Small non-negative number which is added to value of generations.
486
487
		See Also:
488
			* :func:`NiaPy.algorithms.basic.DifferentialEvolution.setParameters`
489
		"""
490
		DifferentialEvolution.setParameters(self, **ukwargs)
491
		self.pmax, self.rp = pmax, rp
492
		if ukwargs: logger.info('Unused arguments: %s' % (ukwargs))
493
494
	def postSelection(self, pop, task, **kwargs):
495
		r"""Post selection operator.
496
497
		In this algorithm the post selection operator decrements the population at specific iterations/generations.
498
499
		Args:
500
			pop (numpy.ndarray[Individual]): Current population.
501
			task (Task): Optimization task.
502
			kwargs (Dict[str, Any]): Additional arguments.
503
504
		Returns:
505
			numpy.ndarray[Individual]: Changed current population.
506
		"""
507
		Gr = task.nFES // (self.pmax * len(pop)) + self.rp
508
		nNP = len(pop) // 2
509
		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)])
510
		return pop
511
512
def proportional(Lt_min, Lt_max, mu, x_f, avg, **args):
513
	r"""Proportional calculation of age of individual.
514
515
	Args:
516
		Lt_min (int): Minimal life time.
517
		Lt_max (int): Maximal life time.
518
		mu (float): Median of life time.
519
		x_f (float): Individuals function/fitness value.
520
		avg (float): Average fitness/function value of current population.
521
		*args (list): Additional arguments.
522
523
	Returns:
524
		int: Age of individual.
525
	"""
526
	return min(Lt_min + mu * avg / x_f, Lt_max)
527
528
def linear(Lt_min, mu, x_f, x_gw, x_gb, **args):
529
	r"""Linear calculation of age of individual.
530
531
	Args:
532
		Lt_min (int): Minimal life time.
533
		Lt_max (int): Maximal life time.
534
		mu (float): Median of life time.
535
		x_f (float): Individual function/fitness value.
536
		avg (float): Average fitness/function value.
537
		x_gw (float): Global worst fitness/function value.
538
		x_gb (float): Global best fitness/function value.
539
		*args (list): Additional arguments.
540
541
	Returns:
542
		int: Age of individual.
543
	"""
544
	return Lt_min + 2 * mu * (x_f - x_gw) / (x_gb - x_gw)
545
546
def bilinear(Lt_min, Lt_max, mu, x_f, avg, x_gw, x_gb, **args):
547
	r"""Bilinear calculation of age of individual.
548
549
	Args:
550
		Lt_min (int): Minimal life time.
551
		Lt_max (int): Maximal life time.
552
		mu (float): Median of life time.
553
		x_f (float): Individual function/fitness value.
554
		avg (float): Average fitness/function value.
555
		x_gw (float): Global worst fitness/function value.
556
		x_gb (float): Global best fitness/function value.
557
		*args (list): Additional arguments.
558
559
	Returns:
560
		int: Age of individual.
561
	"""
562
	if avg < x_f: return Lt_min + mu * (x_f - x_gw) / (x_gb - x_gw)
563
	return 0.5 * (Lt_min + Lt_max) + mu * (x_f - avg) / (x_gb - avg)
564
565
class AgingIndividual(Individual):
566
	r"""Individual with aging.
567
568
	Attributes:
569
		age (int): Age of individual.
570
571
	See Also:
572
		* :class:`NiaPy.algorithms.Individual`
573
	"""
574
	age = 0
575
576
	def __init__(self, **kwargs):
577
		r"""Init Aging Individual.
578
579
		Args:
580
			**kwargs (Dict[str, Any]): Additional arguments sent to parent.
581
582
		See Also:
583
			* :func:`NiaPy.algorithms.Individual.__init__`
584
		"""
585
		Individual.__init__(self, **kwargs)
586
		self.age = 0
587
588
class AgingNpDifferentialEvolution(DifferentialEvolution):
589
	r"""Implementation of Differential evolution algorithm with aging individuals.
590
591
	Algorithm:
592
		Differential evolution algorithm with dynamic population size that is defined by the quality of population
593
594
	Date:
595
		2018
596
597
	Author:
598
		Klemen Berkovič
599
600
	License:
601
		MIT
602
603
	Attributes:
604
		Name (List[str]): list of strings representing algorithm names.
605
		Lt_min (int): Minimal age of individual.
606
		Lt_max (int): Maximal age of individual.
607
		delta_np (float): Proportion of how many individuals shall die.
608
		omega (float): Acceptance rate for individuals to die.
609
		mu (int): Mean of individual max and min age.
610
		age (Callable[[int, int, float, float, float, float, float], int]): Function for calculation of age for individual.
611
612
	See Also:
613
		* :class:`NiaPy.algorithms.basic.DifferentialEvolution`
614
	"""
615
	Name = ['AgingNpDifferentialEvolution', 'ANpDE']
616
617
	@staticmethod
618
	def algorithmInfo():
619
		r"""Get basic information of algorithm.
620
621
		Returns:
622
			str: Basic information of algorithm.
623
624
		See Also:
625
			* :func:`NiaPy.algorithms.Algorithm.algorithmInfo`
626
		"""
627
		return r"""No info"""
628
629
	@staticmethod
630
	def typeParameters():
631
		r"""Get dictionary with functions for checking values of parameters.
632
633
		Returns:
634
			Dict[str, Callable]:
635
				* Lt_min (Callable[[int], bool])
636
				* Lt_max (Callable[[int], bool])
637
				* delta_np (Callable[[float], bool])
638
				* omega (Callable[[float], bool])
639
640
		See Also:
641
			* :func:`NiaPy.algorithms.basic.DifferentialEvolution.typeParameters`
642
		"""
643
		r = DifferentialEvolution.typeParameters()
644
		r.update({
645
			'Lt_min': lambda x: isinstance(x, int) and x >= 0,
646
			'Lt_max': lambda x: isinstance(x, int) and x >= 0,
647
			'delta_np': lambda x: isinstance(x, float) and 0 <= x <= 1,
648
			'omega': lambda x: isinstance(x, float) and 1 >= x >= 0
649
		})
650
		return r
651
652
	def setParameters(self, Lt_min=0, Lt_max=12, delta_np=0.3, omega=0.3, age=proportional, CrossMutt=CrossBest1, **ukwargs):
653
		r"""Set the algorithm parameters.
654
655
		Arguments:
656
			Lt_min (Optional[int]): Minimum life time.
657
			Lt_max (Optional[int]): Maximum life time.
658
			age (Optional[Callable[[int, int, float, float, float, float, float], int]]): Function for calculation of age for individual.
659
660
		See Also:
661
			* :func:`NiaPy.algorithms.basic.DifferentialEvolution.setParameters`
662
		"""
663
		DifferentialEvolution.setParameters(self, itype=AgingIndividual, **ukwargs)
664
		self.Lt_min, self.Lt_max, self.age, self.delta_np, self.omega = Lt_min, Lt_max, age, delta_np, omega
665
		self.mu = abs(self.Lt_max - self.Lt_min) / 2
666
		if ukwargs: logger.info('Unused arguments: %s' % (ukwargs))
667
668
	def deltaPopE(self, t):
669
		r"""Calculate how many individuals are going to dye.
670
671
		Args:
672
			t (int): Number of generations made by the algorithm.
673
674
		Returns:
675
			int: Number of individuals to dye.
676
		"""
677
		return int(self.delta_np * abs(sin(t)))
678
679
	def deltaPopC(self, t):
680
		r"""Calculate how many individuals are going to be created.
681
682
		Args:
683
			t (int): Number of generations made by the algorithm.
684
685
		Returns:
686
			int: Number of individuals to be born.
687
		"""
688
		return int(self.delta_np * abs(cos(t)))
689
690
	def aging(self, task, pop):
691
		r"""Apply aging to individuals.
692
693
		Args:
694
			task (Task): Optimization task.
695
			pop (numpy.ndarray[Individual]): Current population.
696
697
		Returns:
698
			numpy.ndarray[Individual]: New population.
699
		"""
700
		fpop = asarray([x.f for x in pop])
701
		x_b, x_w = pop[argmin(fpop)], pop[argmax(fpop)]
702
		avg, npop = mean(fpop), []
703
		for x in pop:
704
			x.age += 1
705
			Lt = round(self.age(Lt_min=self.Lt_min, Lt_max=self.Lt_max, mu=self.mu, x_f=x.f, avg=avg, x_gw=x_w.f, x_gb=x_b.f))
706
			if x.age <= Lt: npop.append(x)
707
		if len(npop) == 0: npop = objects2array([self.itype(task=task, rnd=self.Rand, e=True) for _ in range(self.NP)])
708
		return npop
709
710
	def popIncrement(self, pop, task):
711
		r"""Increment population.
712
713
		Args:
714
			pop (numpy.ndarray[Individual]): Current population.
715
			task (Task): Optimization task.
716
717
		Returns:
718
			numpy.ndarray[Individual]: Increased population.
719
		"""
720
		deltapop = int(round(max(1, self.NP * self.deltaPopE(task.Iters))))
721
		return objects2array([self.itype(task=task, rnd=self.Rand, e=True) for _ in range(deltapop)])
722
723
	def popDecrement(self, pop, task):
724
		r"""Decrement population.
725
726
		Args:
727
			pop (numpy.ndarray): Current population.
728
			task (Task): Optimization task.
729
730
		Returns:
731
			numpy.ndarray[Individual]: Decreased population.
732
		"""
733
		deltapop = int(round(max(1, self.NP * self.deltaPopC(task.Iters))))
734
		if len(pop) - deltapop <= 0: return pop
735
		ni = self.Rand.choice(len(pop), deltapop, replace=False)
736
		npop = []
737
		for i, e in enumerate(pop):
738
			if i not in ni: npop.append(e)
739
			elif self.rand() >= self.omega: npop.append(e)
740
		return objects2array(npop)
741
742
	def selection(self, pop, npop, task, **kwargs):
743
		r"""Select operator for individuals with aging.
744
745
		Args:
746
			pop (numpy.ndarray[Individual]): Current population.
747
			npop (numpy.ndarray[Individual]): New population.
748
			task (Task): Optimization task.
749
			**kwargs (Dict[str, Any]): Additional arguments.
750
751
		Returns:
752
			numpy.ndarray[Individual]: New population of individuals.
753
		"""
754
		npop = DifferentialEvolution.selection(self, pop, npop)
755
		npop = append(npop, self.popIncrement(pop, task))
756
		pop = self.aging(task, npop)
757
		return pop
758
759
	def postSelection(self, pop, task, xb=None, **kwargs):
760
		r"""Post selection operator.
761
762
		Args:
763
			pop (numpy.ndarray): Current population.
764
			task (Task): Optimization task.
765
			xb (Individual): Global best individual.
766
			**kwargs (Dict[str, Any]): Additional arguments.
767
768
		Returns:
769
			numpy.ndarray[Individual]: New population.
770
		"""
771
		return self.popDecrement(pop, task) if len(pop) > self.NP else pop
772
773
def multiMutations(pop, i, xb, F, CR, rnd, task, itype, strategies, **kwargs):
774
	r"""Mutation strategy that takes more than one strategy and applys them to individual.
775
776
	Args:
777
		pop (numpy.ndarray[Individual]): Current population.
778
		i (int): Index of current individual.
779
		xb (Individual): Current best individual.
780
		F (float): Scale factor.
781
		CR (float): Crossover probability.
782
		rnd (mtrand.RandomState): Random generator.
783
		task (Task): Optimization task.
784
		IndividualType (Individual): Individual type used in algorithm.
785
		strategies (Iterable[Callable[[numpy.ndarray[Individual], int, Individual, float, float, mtrand.RandomState], numpy.ndarray[Individual]]]): List of mutation strategies.
786
		**kwargs (Dict[str, Any]): Additional arguments.
787
788
	Returns:
789
		Individual: Best individual from applyed mutations strategies.
790
	"""
791
	L = [itype(x=strategy(pop, i, xb, F, CR, rnd=rnd), task=task, e=True, rnd=rnd) for strategy in strategies]
792
	return L[argmin([x.f for x in L])]
793
794
class MultiStrategyDifferentialEvolution(DifferentialEvolution):
795
	r"""Implementation of Differential evolution algorithm with multiple mutation strateys.
796
797
	Algorithm:
798
		Implementation of Differential evolution algorithm with multiple mutation strateys
799
800
	Date:
801
		2018
802
803
	Author:
804
		Klemen Berkovič
805
806
	License:
807
		MIT
808
809
	Attributes:
810
		Name (List[str]): List of strings representing algorithm names.
811
		strategies (Iterable[Callable[[numpy.ndarray[Individual], int, Individual, float, float, mtrand.RandomState], numpy.ndarray[Individual]]]): List of mutation strategies.
812
		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.
813
814
	See Also:
815
		* :class:`NiaPy.algorithms.basic.DifferentialEvolution`
816
	"""
817
	Name = ['MultiStrategyDifferentialEvolution', 'MsDE']
818
819
	@staticmethod
820
	def algorithmInfo():
821
		r"""Get basic information of algorithm.
822
823
		Returns:
824
			str: Basic information of algorithm.
825
826
		See Also:
827
			* :func:`NiaPy.algorithms.Algorithm.algorithmInfo`
828
		"""
829
		return r"""No info"""
830
831
	@staticmethod
832
	def typeParameters():
833
		r"""Get dictionary with functions for checking values of parameters.
834
835
		Returns:
836
			Dict[str, Callable]: Testing functions for parameters.
837
838
		See Also:
839
			* :func:`NiaPy.algorithms.basic.DifferentialEvolution.typeParameters`
840
		"""
841
		r = DifferentialEvolution.typeParameters()
842
		r.pop('CrossMutt', None)
843
		r.update({'strategies': lambda x: callable(x)})
844
		return r
845
846
	def setParameters(self, strategies=(CrossRand1, CrossBest1, CrossCurr2Best1, CrossRand2), **ukwargs):
847
		r"""Set the arguments of the algorithm.
848
849
		Arguments:
850
			strategies (Optional[Iterable[Callable[[numpy.ndarray[Individual], int, Individual, float, float, mtrand.RandomState], numpy.ndarray[Individual]]]]): List of mutation strategyis.
851
			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.
852
853
		See Also:
854
			* :func:`NiaPy.algorithms.basic.DifferentialEvolution.setParameters`
855
		"""
856
		DifferentialEvolution.setParameters(self, CrossMutt=multiMutations, **ukwargs)
857
		self.strategies = strategies
858
859 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...
860
		r"""Evolve population with the help multiple mutation strategies.
861
862
		Args:
863
			pop (numpy.ndarray[Individual]): Current population.
864
			xb (Individual): Current best individual.
865
			task (Task): Optimization task.
866
			**kwargs (Dict[str, Any]): Additional arguments.
867
868
		Returns:
869
			numpy.ndarray[Individual]: New population of individuals.
870
		"""
871
		return objects2array([self.CrossMutt(pop, i, xb, self.F, self.CR, self.Rand, task, self.itype, self.strategies) for i in range(len(pop))])
872
873
class DynNpMultiStrategyDifferentialEvolution(MultiStrategyDifferentialEvolution, DynNpDifferentialEvolution):
874
	r"""Implementation of Dynamic population size Differential evolution algorithm with dynamic population size that is defined by the quality of population.
875
876
	Algorithm:
877
		Dynamic population size Differential evolution algorithm with dynamic population size that is defined by the quality of population
878
879
	Date:
880
		2018
881
882
	Author:
883
		Klemen Berkovič
884
885
	License:
886
		MIT
887
888
	Attributes:
889
		Name (List[str]): List of strings representing algorithm name.
890
891
	See Also:
892
		* :class:`NiaPy.algorithms.basic.MultiStrategyDifferentialEvolution`
893
		* :class:`NiaPy.algorithms.basic.DynNpDifferentialEvolution`
894
	"""
895
	Name = ['DynNpMultiStrategyDifferentialEvolution', 'dynNpMsDE']
896
897
	@staticmethod
898
	def algorithmInfo():
899
		r"""Get basic information of algorithm.
900
901
		Returns:
902
			str: Basic information of algorithm.
903
904
		See Also:
905
			* :func:`NiaPy.algorithms.Algorithm.algorithmInfo`
906
		"""
907
		return r"""No info"""
908
909
	@staticmethod
910
	def typeParameters():
911
		r"""Get dictionary with functions for checking values of parameters.
912
913
		Returns:
914
			Dict[str, Callable]:
915
				* rp (Callable[[Union[float, int]], bool]): TODO
916
				* pmax (Callable[[int], bool]): TODO
917
918
		See Also:
919
			* :func:`NiaPy.algorithms.basic.MultiStrategyDifferentialEvolution.typeParameters`
920
		"""
921
		r = MultiStrategyDifferentialEvolution.typeParameters()
922
		r['rp'] = lambda x: isinstance(x, (float, int)) and x > 0
923
		r['pmax'] = lambda x: isinstance(x, int) and x > 0
924
		return r
925
926
	def setParameters(self, **ukwargs):
927
		r"""Set the arguments of the algorithm.
928
929
		Args:
930
			ukwargs (Dict[str, Any]): Additional arguments.
931
932
		See Also:
933
			* :func:`NiaPy.algorithms.basic.MultiStrategyDifferentialEvolution.setParameters`
934
			* :func:`NiaPy.algorithms.basic.DynNpDifferentialEvolution.setParameters`
935
		"""
936
		DynNpDifferentialEvolution.setParameters(self, **ukwargs)
937
		MultiStrategyDifferentialEvolution.setParameters(self, **ukwargs)
938
939
	def evolve(self, pop, xb, task, **kwargs):
940
		return MultiStrategyDifferentialEvolution.evolve(self, pop, xb, task, **kwargs)
941
942
	def postSelection(self, pop, task, **kwargs):
943
		r"""Post selection operator.
944
945
		Args:
946
			pop (numpy.ndarray[Individual]): Current population.
947
			task (Task): Optimization task.
948
			**kwargs (Dict[str, Any]): Additional arguments.
949
950
		Returns:
951
			numpy.ndarray: New population.
952
953
		See Also:
954
			* :func:`NiaPy.algorithms.basic.DynNpDifferentialEvolution.postSelection`
955
		"""
956
		return DynNpDifferentialEvolution.postSelection(self, pop, task)
957
958
class AgingNpMultiMutationDifferentialEvolution(AgingNpDifferentialEvolution, MultiStrategyDifferentialEvolution):
959
	r"""Implementation of Differential evolution algorithm with aging individuals.
960
961
	Algorithm:
962
		Differential evolution algorithm with dynamic population size that is defined by the quality of population
963
964
	Date:
965
		2018
966
967
	Author:
968
		Klemen Berkovič
969
970
	License:
971
		MIT
972
973
	Attributes:
974
		Name (List[str]): List of strings representing algorithm names
975
976
	See Also:
977
		* :class:`NiaPy.algorithms.basic.AgingNpDifferentialEvolution`
978
		* :class:`NiaPy.algorithms.basic.MultiStrategyDifferentialEvolution`
979
	"""
980
	Name = ['AgingNpMultiMutationDifferentialEvolution', 'ANpMSDE']
981
982
	@staticmethod
983
	def algorithmInfo():
984
		r"""Get basic information of algorithm.
985
986
		Returns:
987
			str: Basic information of algorithm.
988
989
		See Also:
990
			* :func:`NiaPy.algorithms.Algorithm.algorithmInfo`
991
		"""
992
		return r"""No info"""
993
994
	@staticmethod
995
	def typeParameters():
996
		r"""Get dictionary with functions for checking values of parameters.
997
998
		Returns:
999
			Dict[str, Callable]: Mappings form parameter names to test functions.
1000
1001
		See Also:
1002
			* :func:`NiaPy.algorithms.basic.MultiStrategyDifferentialEvolution.typeParameters`
1003
			* :func:`NiaPy.algorithms.basic.AgingNpDifferentialEvolution.typeParameters`
1004
		"""
1005
		d = AgingNpDifferentialEvolution.typeParameters()
1006
		d.update(MultiStrategyDifferentialEvolution.typeParameters())
1007
		return d
1008
1009
	def setParameters(self, **ukwargs):
1010
		r"""Set core parameter arguments.
1011
1012
		Args:
1013
			**ukwargs (Dict[str, Any]): Additional arguments.
1014
1015
		See Also:
1016
			* :func:`NiaPy.algorithms.basic.AgingNpDifferentialEvolution.setParameters`
1017
			* :func:`NiaPy.algorithms.basic.MultiStrategyDifferentialEvolution.setParameters`
1018
		"""
1019
		AgingNpDifferentialEvolution.setParameters(self, **ukwargs)
1020
		MultiStrategyDifferentialEvolution.setParameters(self, stratgeys=(CrossRand1, CrossBest1, CrossCurr2Rand1, CrossRand2), itype=AgingIndividual, **ukwargs)
1021
1022
	def evolve(self, pop, xb, task, **kwargs):
1023
		r"""Evolve current population.
1024
1025
		Args:
1026
			pop (numpy.ndarray[Individual]): Current population.
1027
			xb (Individual): Global best individual.
1028
			task (Task): Optimization task.
1029
			**kwargs (Dict[str, Any]): Additional arguments.
1030
1031
		Returns:
1032
			numpy.ndarray[Individual]: New population of individuals.
1033
		"""
1034
		return MultiStrategyDifferentialEvolution.evolve(self, pop, xb, task, **kwargs)
1035
1036
# vim: tabstop=3 noexpandtab shiftwidth=3 softtabstop=3
1037