ForestOptimizationAlgorithm.runIteration()   A
last analyzed

Complexity

Conditions 3

Size

Total Lines 38
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 20
nop 8
dl 0
loc 38
rs 9.4
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
# encoding=utf8
2
import logging
3
from numpy import where, apply_along_axis, zeros, append, ndarray, delete, arange, argmin, absolute, int32
4
from NiaPy.algorithms.algorithm import Algorithm
5
from NiaPy.util import limit_repair
6
7
__all__ = ['ForestOptimizationAlgorithm']
8
9
logging.basicConfig()
10
logger = logging.getLogger('NiaPy.algorithms.basic')
11
logger.setLevel('INFO')
12
13
class ForestOptimizationAlgorithm(Algorithm):
14
	r"""Implementation of Forest Optimization Algorithm.
15
16
	Algorithm:
17
		 Forest Optimization Algorithm
18
19
	Date:
20
		 2019
21
22
	Authors:
23
		 Luka Pečnik
24
25
	License:
26
		 MIT
27
28
	Reference paper:
29
		 Manizheh Ghaemi, Mohammad-Reza Feizi-Derakhshi, Forest Optimization Algorithm, Expert Systems with Applications, Volume 41, Issue 15, 2014, Pages 6676-6687, ISSN 0957-4174, https://doi.org/10.1016/j.eswa.2014.05.009.
30
31
	References URL:
32
		 Implementation is based on the following MATLAB code: https://github.com/cominsys/FOA
33
34
	Attributes:
35
		 Name (List[str]): List of strings representing algorithm name.
36
		 lt (int): Life time of trees parameter.
37
		 al (int): Area limit parameter.
38
		 lsc (int): Local seeding changes parameter.
39
		 gsc (int): Global seeding changes parameter.
40
		 tr (float): Transfer rate parameter.
41
42
	See Also:
43
		 * :class:`NiaPy.algorithms.Algorithm`
44
	"""
45
	Name = ['ForestOptimizationAlgorithm', 'FOA']
46
47
	@staticmethod
48
	def algorithmInfo():
49
		r"""Get algorithms information.
50
51
		Returns:
52
			str: Algorithm information.
53
54
		See Also:
55
			* :func:`NiaPy.algorithms.Algorithm.algorithmInfo`
56
		"""
57
		return r"""Manizheh Ghaemi, Mohammad-Reza Feizi-Derakhshi, Forest Optimization Algorithm, Expert Systems with Applications, Volume 41, Issue 15, 2014, Pages 6676-6687, ISSN 0957-4174, https://doi.org/10.1016/j.eswa.2014.05.009."""
58
59 View Code Duplication
	@staticmethod
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
60
	def typeParameters():
61
		r"""Get dictionary with functions for checking values of parameters.
62
63
		Returns:
64
			 Dict[str, Callable]:
65
				  * lt (Callable[[int], bool]): Checks if life time parameter has a proper value.
66
				  * al (Callable[[int], bool]): Checks if area limit parameter has a proper value.
67
				  * lsc (Callable[[int], bool]): Checks if local seeding changes parameter has a proper value.
68
				  * gsc (Callable[[int], bool]): Checks if global seeding changes parameter has a proper value.
69
				  * tr (Callable[[float], bool]): Checks if transfer rate parameter has a proper value.
70
71
		See Also:
72
			 * :func:`NiaPy.algorithms.algorithm.Algorithm.typeParameters`
73
		"""
74
		d = Algorithm.typeParameters()
75
		d.update({
76
			'lt': lambda x: isinstance(x, int) and x > 0,
77
			'al': lambda x: isinstance(x, int) and x > 0,
78
			'lsc': lambda x: isinstance(x, int) and x > 0,
79
			'gsc': lambda x: isinstance(x, int) and x > 0,
80
			'tr': lambda x: isinstance(x, float) and 0 <= x <= 1,
81
		})
82
		return d
83
84
	def setParameters(self, NP=10, lt=3, al=10, lsc=1, gsc=1, tr=0.3, **ukwargs):
85
		r"""Set the parameters of the algorithm.
86
87
		Args:
88
			 NP (Optional[int]): Population size.
89
			 lt (Optional[int]): Life time parameter.
90
			 al (Optional[int]): Area limit parameter.
91
			 lsc (Optional[int]): Local seeding changes parameter.
92
			 gsc (Optional[int]): Global seeding changes parameter.
93
			 tr (Optional[float]): Transfer rate parameter.
94
			 ukwargs (Dict[str, Any]): Additional arguments.
95
96
		See Also:
97
			 * :func:`NiaPy.algorithms.Algorithm.setParameters`
98
		"""
99
		Algorithm.setParameters(self, NP=NP, **ukwargs)
100
		self.lt, self.al, self.lsc, self.gsc, self.tr = lt, al, lsc, gsc, tr
101
102
	def getParameters(self):
103
		r"""Get parameters values of the algorithm.
104
105
		Returns:
106
			Dict[str, Any]: TODO.
107
		"""
108
		d = Algorithm.getParameters(self)
109
		d.update({
110
			'lt': self.lt,
111
			'al': self.al,
112
			'lsc': self.lsc,
113
			'gsc': self.gsc,
114
			'tr': self.tr
115
		})
116
		return d
117
118
	def localSeeding(self, task, trees):
119
		r"""Local optimum search stage.
120
121
		Args:
122
			 task (Task): Optimization task.
123
			 trees (numpy.ndarray): Zero age trees for local seeding.
124
125
		Returns:
126
			 numpy.ndarray: Resulting zero age trees.
127
		"""
128
		n = trees.shape[0]
129
		deltas = self.uniform(-self.dx, self.dx, (n, self.lsc))
130
		deltas = append(deltas, zeros((n, task.D - self.lsc)), axis=1)
131
		perms = self.rand([deltas.shape[0], deltas.shape[1]]).argsort(1)
132
		deltas = deltas[arange(deltas.shape[0])[:, None], perms]
133
		trees += deltas
134
		trees = apply_along_axis(limit_repair, 1, trees, task.Lower, task.Upper)
135
		return trees
136
137
	def globalSeeding(self, task, candidates, size):
138
		r"""Global optimum search stage that should prevent getting stuck in a local optimum.
139
140
		Args:
141
			 task (Task): Optimization task.
142
			 candidates (numpy.ndarray): Candidate population for global seeding.
143
			 size (int): Number of trees to produce.
144
145
		Returns:
146
			 numpy.ndarray: Resulting trees.
147
		"""
148
		seeds = candidates[self.randint(len(candidates), D=size)]
149
		deltas = self.uniform(task.benchmark.Lower, task.benchmark.Upper, (size, self.gsc))
150
		deltas = append(deltas, zeros((size, task.D - self.gsc)), axis=1)
151
		perms = self.rand([deltas.shape[0], deltas.shape[1]]).argsort(1)
152
		deltas = deltas[arange(deltas.shape[0])[:, None], perms]
153
		deltas = deltas.flatten()
154
		seeds = seeds.flatten()
155
		seeds[deltas != 0] = deltas[deltas != 0]
156
157
		return seeds.reshape(size, task.D)
158
159
	def removeLifeTimeExceeded(self, trees, candidates, age):
160
		r"""Remove dead trees.
161
162
		Args:
163
			 trees (numpy.ndarray): Population to test.
164
			 candidates (numpy.ndarray): Candidate population array to be updated.
165
			 age (numpy.ndarray[int32]): Age of trees.
166
167
		Returns:
168
			 Tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray[int32]]:
169
				  1. Alive trees.
170
				  2. New candidate population.
171
				  3. Age of trees.
172
		"""
173
		lifeTimeExceeded = where(age > self.lt)
174
		candidates = trees[lifeTimeExceeded]
175
		trees = delete(trees, lifeTimeExceeded, axis=0)
176
		age = delete(age, lifeTimeExceeded, axis=0)
177
		return trees, candidates, age
178
179
	def survivalOfTheFittest(self, task, trees, candidates, age):
180
		r"""Evaluate and filter current population.
181
182
		Args:
183
			 task (Task): Optimization task.
184
			 trees (numpy.ndarray): Population to evaluate.
185
			 candidates (numpy.ndarray): Candidate population array to be updated.
186
			 age (numpy.ndarray[int32]): Age of trees.
187
188
		Returns:
189
			 Tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray[float], numpy.ndarray[int32]]:
190
				  1. Trees sorted by fitness value.
191
				  2. Updated candidate population.
192
				  3. Population fitness values.
193
				  4. Age of trees
194
		"""
195
		evaluations = apply_along_axis(task.eval, 1, trees)
196
		ei = evaluations.argsort()
197
		candidates = append(candidates, trees[ei[self.al:]], axis=0)
198
		trees = trees[ei[:self.al]]
199
		age = age[ei[:self.al]]
200
		evaluations = evaluations[ei[:self.al]]
201
		return trees, candidates, evaluations, age
202
203
	def initPopulation(self, task):
204
		r"""Initialize the starting population.
205
206
		Args:
207
			 task (Task): Optimization task
208
209
		Returns:
210
			 Tuple[numpy.ndarray, numpy.ndarray[float], Dict[str, Any]]:
211
				  1. New population.
212
				  2. New population fitness/function values.
213
				  3. Additional arguments:
214
						* age (numpy.ndarray[int32]): Age of trees.
215
216
		See Also:
217
			 * :func:`NiaPy.algorithms.Algorithm.initPopulation`
218
		"""
219
		Trees, Evaluations, _ = Algorithm.initPopulation(self, task)
220
		age = zeros(self.NP, dtype=int32)
221
		self.dx = absolute(task.benchmark.Upper) / 5
222
		return Trees, Evaluations, {'age': age}
223
224
	def runIteration(self, task, Trees, Evaluations, xb, fxb, age, **dparams):
225
		r"""Core function of Forest Optimization Algorithm.
226
227
		Args:
228
			 task (Task): Optimization task.
229
			 Trees (numpy.ndarray): Current population.
230
			 Evaluations (numpy.ndarray[float]): Current population function/fitness values.
231
			 xb (numpy.ndarray): Global best individual.
232
			 fxb (float): Global best individual fitness/function value.
233
			 age (numpy.ndarray[int32]): Age of trees.
234
			 **dparams (Dict[str, Any]): Additional arguments.
235
236
		Returns:
237
			 Tuple[numpy.ndarray, numpy.ndarray[float], Dict[str, Any]]:
238
				  1. New population.
239
				  2. New population fitness/function values.
240
				  3. Additional arguments:
241
						* age (numpy.ndarray[int32]): Age of trees.
242
		"""
243
		candidatePopulation = ndarray((0, task.D + 1))
244
		zeroAgeTrees = Trees[age == 0]
245
		localSeeds = self.localSeeding(task, zeroAgeTrees)
246
		age += 1
247
		Trees, candidatePopulation, age = self.removeLifeTimeExceeded(Trees, candidatePopulation, age)
248
		Trees = append(Trees, localSeeds, axis=0)
249
		age = append(age, zeros(len(localSeeds), dtype=int32))
250
		Trees, candidatePopulation, Evaluations, age = self.survivalOfTheFittest(task, Trees, candidatePopulation, age)
251
		gsn = int(self.tr * len(candidatePopulation))
252
		if gsn > 0:
253
			globalSeeds = self.globalSeeding(task, candidatePopulation, gsn)
254
			Trees = append(Trees, globalSeeds, axis=0)
255
			age = append(age, zeros(len(globalSeeds), dtype=int32))
256
			gste = apply_along_axis(task.eval, 1, globalSeeds)
257
			Evaluations = append(Evaluations, gste)
258
		ib = argmin(Evaluations)
259
		age[ib] = 0
260
		if Evaluations[ib] < fxb: xb, fxb = Trees[ib].copy(), Evaluations[ib]
261
		return Trees, Evaluations, xb, fxb, {'age': age}
262