Source code for pychemia.searcher.firefly

import math
import numpy as np

from .searcher import Searcher
from pychemia import pcm_log


[docs]class FireFly(Searcher): def __init__(self, population, params=None, generation_size=32, stabilization_limit=10, target_value=None): """ 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. For Firefly the parameters are: 'beta0': A value in the range (0,1) that defines how close the mobile structure will be from to the target structure 'gamma': Defines the exponent of decreasing for the movement of the mobile structure 'alpha0': Factor of scale for the random movement 'delta': How the random change decreases with time 'multi_move': Boolean to express if the fireflies moves following all the other brighter ones or just the closest brigter firefly :param generation_size: (int) :param stabilization_limit: (int) :return: """ # Initializing objects Searcher.__init__(self, population, generation_size, stabilization_limit, target_value) # Parameters self.gamma = None self.beta0 = None self.alpha0 = None self.delta = None self.multi_move = None self.set_params(params)
[docs] def set_params(self, params): # Default parameters self.gamma = 0.1 self.beta0 = 0.9 self.alpha0 = 0.3 self.delta = 0.9 self.multi_move = True if params is None: params = {} if 'gamma' in params: self.gamma = params['gamma'] if 'beta0' in params: self.beta0 = params['beta0'] if 'alpha0' in params: self.alpha0 = params['alpha0'] if 'delta' in params: self.delta = params['delta'] if 'multi_move' in params: self.multi_move = params['multi_move']
[docs] def get_params(self): return {'gamma': self.gamma, 'beta0': self.beta0, 'alpha0': self.alpha0, 'delta': self.delta, 'multi_move': self.multi_move}
[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) pcm_log.info(' Size of selection : %d' % len(selection)) # For statistical purposes distances = [] intensities = [] atractiveness = [] # 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 moves[entry_id] = 0 # 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]])) for i in range(1, len(selection)): entry_id = selection[i] pcm_log.debug('Moving %d %s. Intensity: %7.3f' % (i, str(entry_id), intensity[entry_id])) # Moving in the direction of all the brighter fireflies if self.multi_move: for j in list(range(0, i))[::-1]: entry_jd = selection[j] distance = self.population.distance(entry_id, entry_jd) beta = self.beta0 * math.exp(-self.gamma * distance * distance) # The variation of attractiveness \beta with the distance r pcm_log.debug('[%s] Distance: %7.3f. Intensity: %7.3f. Atractiveness: %7.3f' % (str(entry_jd), distance, intensity[entry_jd], beta)) # Collecting Statistics distances.append(distance) intensities.append(intensity[entry_jd]) atractiveness.append(beta) if new_selection[entry_id] is None: new_selection[entry_id] = self.population.move(entry_id, entry_jd, factor=beta, in_place=False) if self.alpha0 > 0: factor = self.alpha0 * (self.delta ** self.current_generation) self.population.move_random(new_selection[entry_id], factor=factor, in_place=True) else: self.population.move(new_selection[entry_id], entry_jd, factor=beta, in_place=True) if self.alpha0 > 0: factor = self.alpha0 * (self.delta ** self.current_generation) self.population.move_random(new_selection[entry_id], factor=factor, in_place=True) # print(new_selection) moves[entry_id] += 1 # Moving in the direction of the closets brighter firefly else: distances = [self.population.distance(entry_id, entry_jd) for entry_jd in selection[:i]] distance = min(distances) target = selection[distances.index(distance)] beta = self.beta0 * math.exp(-self.gamma * distance * distance) # The variation of attractiveness \beta with the distance r pcm_log.debug('[%s] Distance: %7.3f. Intensity: %7.3f. Atractiveness: %7.3f' % (str(entry_jd), distance, intensity[entry_jd], beta)) new_selection[entry_id] = self.population.move(entry_id, target, factor=beta, in_place=False) factor = self.alpha0 * (self.delta ** self.current_generation) self.population.move_random(new_selection[entry_id], factor=factor, in_place=True) moves[entry_id] += 1 if len(distances) > 0: pcm_log.info('+----------------+--------------+-------------+-------------+') pcm_log.info('+ | Minimum | Maximum | Average |') pcm_log.info('+----------------+--------------+-------------+-------------+') pcm_log.info('+ Distances | %7.2f | %7.2f | %7.2f |' % (np.min(distances), np.max(distances), np.average(distances))) pcm_log.info('+ Intensities | %7.2f | %7.2f | %7.2f |' % (np.min(intensities), np.max(intensities), np.average(intensities))) pcm_log.info('+ Attractiveness | %7.2f | %7.2f | %7.2f |' % (np.min(atractiveness), np.max(atractiveness), np.average(atractiveness))) pcm_log.info('+----------------+--------------+-------------+-------------+') for entry_id in selection: if new_selection[entry_id] is not None: self.replace_by_other(entry_id, new_selection[entry_id], reason='Moved %d times' % moves[entry_id]) self.population.enable(new_selection[entry_id]) else: self.pass_to_new_generation(entry_id, reason='The best')