Passed
Branch master (13db34)
by Grega
02:16
created

DynNpDifferentialEvolution.setParameters()   A

Complexity

Conditions 2

Size

Total Lines 13
Code Lines 4

Duplication

Lines 13
Ratio 100 %

Importance

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