import math
from .searcher import Searcher
from pychemia import pcm_log
[docs]class ParticleSwarm(Searcher):
def __init__(self, population, params=None, generation_size=32, stabilization_limit=10):
"""
Implementation fo the Firefly algorithm for global minimization
This searcher uses a metric to compute the attractiveness and the vector displacement
to move one firefly in the direction of another one
:param population:
:param params: (dict) Parameters to setup the Searcher
:param generation_size: (int)
:param stabilization_limit: (int)
:return:
"""
# Mandatory objects
self.population = population
# Parameters
self.gamma = None
self.elites = None
self.set_params(params)
# Constrains
self.generation_size = generation_size
self.stabilization_limit = stabilization_limit
# Initializing objects
Searcher.__init__(self, self.population, generation_size, stabilization_limit)
[docs] def set_params(self, params):
if params is None:
self.gamma = 0.1
self.elites = 3
else:
assert ('gamma' in params)
assert (params['gamma'] >= 0.0)
self.gamma = params['gamma']
if 'elites' in params:
self.elites = params['elites']
[docs] def get_params(self):
return {'gamma': self.gamma, 'elites': self.elites}
[docs] def run_one(self):
# Get a static selection of the values in the generation that are relaxed
selection = self.population.ids_sorted(self.actives_in_generation)
# Minus sign because we are searching for minima
intensity = self.population.get_values(selection)
for entry_id in intensity:
intensity[entry_id] *= -1
moves = {}
new_selection = {}
for entry_id in selection:
new_selection[entry_id] = None
# Move all the fireflies (Except the most brightness)
# as the selection is sorted it means that the first one will no move
pcm_log.debug('No Moving %d %s. Intensity: %7.3f' % (0, str(selection[0]), intensity[selection[0]]))
# The best
elites = selection[:self.elites]
for i in range(self.elites, len(selection)):
entry_id = selection[i]
pcm_log.debug('Moving %d %s. Intensity: %7.3f' % (i, str(entry_id), intensity[entry_id]))
distances = [self.population.distance(entry_id, entry_jd) for entry_jd in elites]
target = elites[distances.index(min(distances))]
distance = min(distances)
atractiveness = math.exp(-self.gamma * distance) * intensity[target]
pcm_log.debug('[%s] Distance: %7.3f. Intensity: %7.3f. Atractiveness: %7.3f' % (str(target),
distance,
intensity[target],
atractiveness))
if intensity[entry_id] < atractiveness:
new_selection[entry_id] = self.population.move(entry_id, target, in_place=False)
for entry_id in selection:
pcm_log.debug('Deciding fate for firefly: %s' % str(entry_id))
if new_selection[entry_id] is not None:
pcm_log.debug('Moved to a new location %s ' % str(entry_id))
self.replace_by_other(entry_id, new_selection[entry_id], reason=None)
else:
pcm_log.debug('Promoted to new generation ')
self.pass_to_new_generation(entry_id, reason='No other firefly is more attractive')