NiaPy.algorithms.basic.fss   A
last analyzed

Complexity

Total Complexity 39

Size/Duplication

Total Lines 357
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 125
dl 0
loc 357
rs 9.28
c 0
b 0
f 0
wmc 39

19 Methods

Rating   Name   Duplication   Size   Complexity  
A Fish.__init__() 0 15 1
A FishSchoolSearch.algorithmInfo() 0 11 1
A FishSchoolSearch.collective_volitive_movement() 0 23 3
A FishSchoolSearch.gen_weight() 0 7 1
A FishSchoolSearch.feeding() 0 14 5
A FishSchoolSearch.setParameters() 0 22 1
A FishSchoolSearch.init_fish() 0 11 1
A FishSchoolSearch.max_delta_cost() 0 13 3
A FishSchoolSearch.getParameters() 0 19 1
A FishSchoolSearch.update_steps() 0 12 1
A FishSchoolSearch.init_school() 0 14 2
A FishSchoolSearch.runIteration() 0 24 1
A FishSchoolSearch.total_school_weight() 0 14 1
A FishSchoolSearch.individual_movement() 0 29 4
A FishSchoolSearch.calculate_barycenter() 0 17 4
A FishSchoolSearch.collective_instinctive_movement() 0 16 4
A FishSchoolSearch.initPopulation() 0 11 1
A FishSchoolSearch.generate_uniform_coordinates() 0 10 1
A FishSchoolSearch.typeParameters() 0 4 3
1
# encoding=utf8
2
from numpy import nan, asarray, zeros, float, full
3
4
from NiaPy.util.utility import objects2array
5
from NiaPy.algorithms.algorithm import Algorithm, Individual
6
7
class Fish(Individual):
8
	r"""Fish individual class.
9
10
	Attributes:
11
		weight (float): Weight of fish.
12
		delta_pos (float): TODO.
13
		delta_cost (float): TODO.
14
		has_improved (bool): If the fish has improved.
15
16
	See Also:
17
		* :class:`NiaPy.algorithms.algorithm.Individual`
18
	"""
19
	def __init__(self, weight, **kwargs):
20
		r"""Initialize fish individual.
21
22
		Args:
23
			weight (float): Weight of fish.
24
			**kwargs (Dict[str, Any]): Additional arguments.
25
26
		See Also:
27
			* :func:`NiaPy.algorithms.algorithm.Individual`
28
		"""
29
		Individual.__init__(self, **kwargs)
30
		self.weight = weight
31
		self.delta_pos = nan
32
		self.delta_cost = nan
33
		self.has_improved = False
34
35
class FishSchoolSearch(Algorithm):
36
	r"""Implementation of Fish School Search algorithm.
37
38
	Algorithm:
39
		Fish School Search algorithm
40
41
	Date:
42
		2019
43
44
	Authors:
45
		Clodomir Santana Jr, Elliackin Figueredo, Mariana Maceds, Pedro Santos.
46
		Ported to NiaPy with small changes by Kristian Järvenpää (2018).
47
		Ported to the NiaPy 2.0 by Klemen Berkovič (2019).
48
49
	License:
50
		MIT
51
52
	Reference paper:
53
		Bastos Filho, Lima Neto, Lins, D. O. Nascimento and P. Lima, “A novel search algorithm based on fish school behavior,” in 2008 IEEE International Conference on Systems, Man and Cybernetics, Oct 2008, pp. 2646–2651.
54
55
	Attributes:
56
		Name (List[str]): List of strings representing algorithm name.
57
		SI_init (int): Length of initial individual step.
58
		SI_final (int): Length of final individual step.
59
		SV_init (int): Length of initial volatile step.
60
		SV_final (int): Length of final volatile step.
61
		min_w (float): Minimum weight of a fish.
62
		w_scale (float): Maximum weight of a fish.
63
64
	See Also:
65
		* :class:`NiaPy.algorithms.algorithm.Algorithm`
66
	"""
67
	Name = ['FSS', 'FishSchoolSearch']
68
69
	@staticmethod
70
	def algorithmInfo():
71
		r"""Get default information of algorithm.
72
73
		Returns:
74
			str: Basic information.
75
76
		See Also:
77
			* :func:`NiaPy.algorithms.Algorithm.algorithmInfo`
78
		"""
79
		return r"""Bastos Filho, Lima Neto, Lins, D. O. Nascimento and P. Lima, “A novel search algorithm based on fish school behavior,” in 2008 IEEE International Conference on Systems, Man and Cybernetics, Oct 2008, pp. 2646–2651."""
80
81
	@staticmethod
82
	def typeParameters():
83
		# TODO
84
		return {'school_size': lambda x: False, 'SI_final': lambda x: False}
85
86
	def setParameters(self, NP=25, SI_init=3, SI_final=10, SV_init=3, SV_final=13, min_w=0.3, w_scale=0.7, **ukwargs):
87
		r"""Set core arguments of FishSchoolSearch algorithm.
88
89
		Arguments:
90
			NP (Optional[int]): Number of fishes in school.
91
			SI_init (Optional[int]): Length of initial individual step.
92
			SI_final (Optional[int]): Length of final individual step.
93
			SV_init (Optional[int]): Length of initial volatile step.
94
			SV_final (Optional[int]): Length of final volatile step.
95
			min_w (Optional[float]): Minimum weight of a fish.
96
			w_scale (Optional[float]): Maximum weight of a fish.
97
98
		See Also:
99
			* :func:`NiaPy.algorithms.Algorithm.setParameters`
100
		"""
101
		Algorithm.setParameters(self, NP=NP, **ukwargs)
102
		self.step_individual_init = SI_init
103
		self.step_individual_final = SI_final
104
		self.step_volitive_init = SV_init
105
		self.step_volitive_final = SV_final
106
		self.min_w = min_w
107
		self.w_scale = w_scale
108
109
	def getParameters(self):
110
		r"""Get algorithm parameters.
111
112
		Returns:
113
			Dict[str, Any]: TODO.
114
115
		See Also:
116
			* :func:`NiaPy.algorithms.Algorithm.setParameters`
117
		"""
118
		d = Algorithm.getParameters(self)
119
		d.update({
120
			'SI_init': self.step_individual_init,
121
			'SI_final': self.step_individual_final,
122
			'SV_init': self.step_volitive_init,
123
			'SV_final': self.step_volitive_final,
124
			'min_w': self.min_w,
125
			'w_scale': self.w_scale
126
		})
127
		return d
128
129
	def generate_uniform_coordinates(self, task):
130
		r"""Return Numpy array with uniform distribution.
131
132
		Args:
133
			task (Task): Optimization task.
134
135
		Returns:
136
			numpy.ndarray: Array with uniform distribution.
137
		"""
138
		return asarray([self.uniform(task.Lower, task.Upper, task.D) for _ in range(self.NP)])
139
140
	def gen_weight(self):
141
		r"""Get initial weight for fish.
142
143
		Returns:
144
			float: Weight for fish.
145
		"""
146
		return self.w_scale / 2.0
147
148
	def init_fish(self, pos, task):
149
		r"""Create a new fish to a given position.
150
151
		Args:
152
			pos:
153
			task (Task):
154
155
		Returns:
156
157
		"""
158
		return Fish(x=pos, weight=self.gen_weight(), task=task, e=True)
159
160
	def init_school(self, task):
161
		"""Initialize fish school with uniform distribution."""
162
		curr_step_individual = self.step_individual_init * (task.Upper - task.Lower)
163
		curr_step_volitive = self.step_volitive_init * (task.Upper - task.Lower)
164
		curr_weight_school = 0.0
165
		prev_weight_school = 0.0
166
		school = []
167
		positions = self.generate_uniform_coordinates(task)
168
		for idx in range(self.NP):
169
			fish = self.init_fish(positions[idx], task)
170
			school.append(fish)
171
			curr_weight_school += fish.weight
172
		prev_weight_school = curr_weight_school
173
		return curr_step_individual, curr_step_volitive, curr_weight_school, prev_weight_school, objects2array(school)
174
175
	def max_delta_cost(self, school):
176
		r"""Find maximum delta cost - return 0 if none of the fishes moved.
177
178
		Args:
179
			school (numpy.ndarray):
180
181
		Returns:
182
183
		"""
184
		max_ = 0
185
		for fish in school:
186
			if max_ < fish.delta_cost: max_ = fish.delta_cost
187
		return max_
188
189
	def total_school_weight(self, school, prev_weight_school, curr_weight_school):
190
		r"""Calculate and update current weight of fish school.
191
192
		Args:
193
			school (numpy.ndarray):
194
			prev_weight_school (numpy.ndarray):
195
			curr_weight_school (numpy.ndarray):
196
197
		Returns:
198
			Tuple[numpy.ndarray, numpy.ndarray]: TODO.
199
		"""
200
		prev_weight_school = curr_weight_school
201
		curr_weight_school = sum(fish.weight for fish in school)
202
		return prev_weight_school, curr_weight_school
203
204
	def calculate_barycenter(self, school, task):
205
		r"""Calculate barycenter of fish school.
206
207
		Args:
208
			school (numpy.ndarray): Current school fish.
209
			task (Task): Optimization task.
210
211
		Returns:
212
			numpy.ndarray: TODO.
213
		"""
214
		barycenter = zeros((task.D,), dtype=float)
215
		density = 0.0
216
		for fish in school:
217
			density += fish.weight
218
			for dim in range(task.D): barycenter[dim] += (fish.x[dim] * fish.weight)
219
		for dim in range(task.D): barycenter[dim] = barycenter[dim] / density
220
		return barycenter
221
222
	def update_steps(self, task):
223
		r"""Update step length for individual and volatile steps.
224
225
		Args:
226
			task (Task): Optimization task
227
228
		Returns:
229
			Tuple[numpy.ndarray, numpy.ndarray]: TODO.
230
		"""
231
		curr_step_individual = full(task.D, self.step_individual_init - task.Iters * float(self.step_individual_init - self.step_individual_final) / task.nGEN)
232
		curr_step_volitive = full(task.D, self.step_volitive_init - task.Iters * float(self.step_volitive_init - self.step_volitive_final) / task.nGEN)
233
		return curr_step_individual, curr_step_volitive
234
235
	def feeding(self, school):
236
		r"""Feed all fishes.
237
238
		Args:
239
			school (numpy.ndarray): Current school fish population.
240
241
		Returns:
242
			numpy.ndarray: New school fish population.
243
		"""
244
		for fish in school:
245
			if self.max_delta_cost(school): fish.weight = fish.weight + (fish.delta_cost / self.max_delta_cost(school))
246
			if fish.weight > self.w_scale: fish.weight = self.w_scale
247
			elif fish.weight < self.min_w: fish.weight = self.min_w
248
		return school
249
250
	def individual_movement(self, school, curr_step_individual, xb, fxb, task):
251
		r"""Perform individual movement for each fish.
252
253
		Args:
254
			school (numpy.ndarray): School fish population.
255
			curr_step_individual (numpy.ndarray): TODO
256
			xb (numpy.ndarray): Global best solution.
257
			fxb (float): Global best solutions fitness/objecive value.
258
			task (Task): Optimization task.
259
260
		Returns:
261
			Tuple[numpy.ndarray, numpy.ndarray, float]:
262
				1. New school of fishes.
263
		"""
264
		for fish in school:
265
			new_pos = task.repair(fish.x + (curr_step_individual * self.uniform(-1, 1, task.D)), rnd=self.Rand)
266
			cost = task.eval(new_pos)
267
			if cost < fish.f:
268
				xb, fxb = self.getBest(new_pos, cost, xb, fxb)
269
				fish.delta_cost = abs(cost - fish.f)
270
				fish.f = cost
271
				delta_pos = zeros((task.D,), dtype=float)
272
				for idx in range(task.D): delta_pos[idx] = new_pos[idx] - fish.x[idx]
273
				fish.delta_pos = delta_pos
274
				fish.x = new_pos
275
			else:
276
				fish.delta_pos = zeros((task.D,), dtype=float)
277
				fish.delta_cost = 0
278
		return school, xb, fxb
279
280
	def collective_instinctive_movement(self, school, task):
281
		r"""Perform collective instinctive movement.
282
283
		Args:
284
			school (numpy.ndarray): Current population.
285
			task (Task): Optmization task.
286
287
		Returns:
288
			numpy.ndarray: New populaiton
289
		"""
290
		cost_eval_enhanced = zeros((task.D,), dtype=float)
291
		density = sum([f.delta_cost for f in school])
292
		for fish in school: cost_eval_enhanced += (fish.delta_pos * fish.delta_cost)
293
		if density != 0: cost_eval_enhanced = cost_eval_enhanced / density
294
		for fish in school: fish.x = task.repair(fish.x + cost_eval_enhanced, rnd=self.Rand)
295
		return school
296
297
	def collective_volitive_movement(self, school, curr_step_volitive, prev_weight_school, curr_weight_school, xb, fxb, task):
298
		r"""Perform collective volitive movement.
299
300
		Args:
301
			school (numpy.ndarray):
302
			curr_step_volitive :
303
			prev_weight_school:
304
			curr_weight_school:
305
			xb (numpy.ndarray): Global best solution.
306
			fxb (float): Global best solutions fitness/objective value.
307
			task (Task): Optimization task.
308
309
		Returns:
310
			Tuple[numpy.ndarray, numpy.ndarray, float]: New population.
311
		"""
312
		prev_weight_school, curr_weight_school = self.total_school_weight(school=school, prev_weight_school=prev_weight_school, curr_weight_school=curr_weight_school)
313
		barycenter = self.calculate_barycenter(school, task)
314
		for fish in school:
315
			if curr_weight_school > prev_weight_school: fish.x -= (fish.x - barycenter) * curr_step_volitive * self.uniform(0, 1, task.D)
316
			else: fish.x += (fish.x - barycenter) * curr_step_volitive * self.uniform(0, 1, task.D)
317
			fish.evaluate(task, rnd=self.Rand)
318
			xb, fxb = self.getBest(fish.x, fish.f, xb, fxb)
319
		return school, xb, fxb
320
321
	def initPopulation(self, task):
322
		r"""Initialize the school.
323
324
		Args:
325
			task (Task): Optimization task.
326
327
		Returns:
328
			Tuple[numpy.ndarray, numpy.ndarray, dict]: TODO.
329
		"""
330
		curr_step_individual, curr_step_volitive, curr_weight_school, prev_weight_school, school = self.init_school(task)
331
		return school, asarray([f.f for f in school]), {'curr_step_individual': curr_step_individual, 'curr_step_volitive': curr_step_volitive, 'curr_weight_school': curr_weight_school, 'prev_weight_school': prev_weight_school}
332
333
	def runIteration(self, task, school, fschool, xb, fxb, curr_step_individual, curr_step_volitive, curr_weight_school, prev_weight_school, **dparams):
334
		r"""Core function of algorithm.
335
336
		Args:
337
			task (Task):
338
			school (numpy.ndarray):
339
			fschool (numpy.ndarray):
340
			best_fish (numpy.ndarray):
341
			fxb (float):
342
			curr_step_individual:
343
			curr_step_volitive:
344
			curr_weight_school:
345
			prev_weight_school:
346
			**dparams:
347
348
		Returns:
349
			Tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray, float, dict]: TODO.
350
		"""
351
		school, xb, fxb = self.individual_movement(school, curr_step_individual, xb, fxb, task)
352
		school = self.feeding(school)
353
		school = self.collective_instinctive_movement(school, task)
354
		school, xb, fxb = self.collective_volitive_movement(school=school, curr_step_volitive=curr_step_volitive, prev_weight_school=prev_weight_school, curr_weight_school=curr_weight_school, xb=xb, fxb=fxb, task=task)
355
		curr_step_individual, curr_step_volitive = self.update_steps(task)
356
		return school, asarray([f.f for f in school]), xb, fxb, {'curr_step_individual': curr_step_individual, 'curr_step_volitive': curr_step_volitive, 'curr_weight_school': curr_weight_school, 'prev_weight_school': prev_weight_school}
357
358
# vim: tabstop=3 noexpandtab shiftwidth=3 softtabstop=3
359