Source code for niapy.algorithms.basic.mbo

# encoding=utf8
import logging

import numpy as np

from niapy.algorithms.algorithm import Algorithm

__all__ = ['MonarchButterflyOptimization']

logging.basicConfig()
logger = logging.getLogger('niapy.algorithms.basic')
logger.setLevel('INFO')


[docs]class MonarchButterflyOptimization(Algorithm): r"""Implementation of Monarch Butterfly Optimization. Algorithm: Monarch Butterfly Optimization Date: 2019 Authors: Jan Banko License: MIT Reference paper: Wang, G. G., Deb, S., & Cui, Z. (2019). Monarch butterfly optimization. Neural computing and applications, 31(7), 1995-2014. Attributes: Name (List[str]): List of strings representing algorithm name. PAR (float): Partition. PER (float): Period. See Also: * :class:`niapy.algorithms.Algorithm` """ Name = ['MonarchButterflyOptimization', 'MBO']
[docs] @staticmethod def info(): r"""Get information of the algorithm. Returns: str: Algorithm information. See Also: * :func:`niapy.algorithms.algorithm.Algorithm.info` """ return r"""Description: Monarch butterfly optimization algorithm is inspired by the migration behaviour of the monarch butterflies in nature. Authors: Wang, Gai-Ge & Deb, Suash & Cui, Zhihua. Year: 2015 Main reference: Wang, G. G., Deb, S., & Cui, Z. (2019). Monarch butterfly optimization. Neural computing and applications, 31(7), 1995-2014."""
[docs] def __init__(self, population_size=20, partition=5.0 / 12.0, period=1.2, *args, **kwargs): """Initialize MonarchButterflyOptimization. Args: population_size (Optional[int]): Population size. partition (Optional[int]): Partition. period (Optional[int]): Period. See Also: * :func:`niapy.algorithms.Algorithm.__init__` """ super().__init__(population_size, *args, **kwargs) self.partition = partition self.period = period self.keep = 2 self.bar = partition self.np1 = int(np.ceil(partition * population_size)) self.np2 = population_size - self.np1
[docs] def set_parameters(self, population_size=20, partition=5.0 / 12.0, period=1.2, **kwargs): r"""Set the parameters of the algorithm. Args: population_size (Optional[int]): Population size. partition (Optional[int]): Partition. period (Optional[int]): Period. See Also: * :func:`niapy.algorithms.Algorithm.set_parameters` """ super().set_parameters(population_size=population_size, **kwargs) self.partition = partition self.period = period self.keep = 2 self.bar = partition self.np1 = int(np.ceil(partition * population_size)) self.np2 = population_size - self.np1
[docs] def get_parameters(self): r"""Get parameters values for the algorithm. Returns: Dict[str, Any]: Algorithm parameters. """ d = Algorithm.get_parameters(self) d.update({ 'partition': self.partition, 'period': self.period, 'keep': self.keep, 'bar': self.bar, 'np1': self.np1, 'np2': self.np2 }) return d
[docs] def levy(self, _step_size, dimension): r"""Calculate levy flight. Args: _step_size (float): Size of the walk step. dimension (int): Number of dimensions. Returns: numpy.ndarray: Calculated values for levy flight. """ delta_x = np.array([np.sum(np.tan(np.pi * self.uniform(0.0, 1.0, 10))) for _ in range(dimension)]) return delta_x
[docs] def migration_operator(self, dimension, np1, np2, butterflies): r"""Apply the migration operator. Args: dimension (int): Number of dimensions. np1 (int): Number of butterflies in Land 1. np2 (int): Number of butterflies in Land 2. butterflies (numpy.ndarray): Current butterfly population. Returns: numpy.ndarray: Adjusted butterfly population. """ pop1 = np.copy(butterflies[:np1]) pop2 = np.copy(butterflies[np1:]) for k1 in range(np1): for i in range(dimension): r1 = self.random() * self.period if r1 <= self.partition: r2 = self.integers(np1 - 1) butterflies[k1, i] = pop1[r2, i] else: r3 = self.integers(np2 - 1) butterflies[k1, i] = pop2[r3, i] return butterflies
[docs] def adjusting_operator(self, t, max_t, dimension, np1, np2, butterflies, best): r"""Apply the adjusting operator. Args: t (int): Current generation. max_t (int): Maximum generation. dimension (int): Number of dimensions. np1 (int): Number of butterflies in Land 1. np2 (int): Number of butterflies in Land 2. butterflies (numpy.ndarray): Current butterfly population. best (numpy.ndarray): The best butterfly currently. Returns: numpy.ndarray: Adjusted butterfly population. """ pop2 = np.copy(butterflies[np1:]) for k2 in range(np1, np1 + np2): scale = 1.0 / ((t + 1) ** 2) step_size = np.ceil(self.rng.exponential(2 * max_t)) delta_x = self.levy(step_size, dimension) for i in range(dimension): if self.uniform(0.0, 1.0) >= self.partition: butterflies[k2, i] = best[i] else: r4 = self.integers(np2 - 1) butterflies[k2, i] = pop2[r4, 1] if self.uniform(0.0, 1.0) > self.bar: butterflies[k2, i] += scale * (delta_x[i] - 0.5) return butterflies
[docs] @staticmethod def evaluate_and_sort(task, butterflies): r"""Evaluate and sort the butterfly population. Args: task (Task): Optimization task butterflies (numpy.ndarray): Current butterfly population. Returns: numpy.ndarray: Tuple[numpy.ndarray, float, numpy.ndarray]: 1. Best butterfly according to the evaluation. 2. The best fitness value. 3. Butterfly population. """ fitness = np.apply_along_axis(task.eval, 1, butterflies) indices = np.argsort(fitness) butterflies = butterflies[indices] fitness = fitness[indices] return fitness, butterflies
[docs] def init_population(self, task): r"""Initialize the starting population. Args: task (Task): Optimization task Returns: Tuple[numpy.ndarray, numpy.ndarray[float], Dict[str, Any]]: 1. New population. 2. New population fitness/function values. 3. Additional arguments: * current_best (numpy.ndarray): Current generation's best individual. See Also: * :func:`niapy.algorithms.Algorithm.init_population` """ population, fitness, _ = super().init_population(task) sorted_indices = np.argsort(fitness) population = population[sorted_indices] fitness = fitness[sorted_indices] return population, fitness, {'current_best': population[0]}
[docs] def run_iteration(self, task, population, population_fitness, best_x, best_fitness, **params): r"""Core function of Forest Optimization Algorithm. Args: task (Task): Optimization task. population (numpy.ndarray): Current population. population_fitness (numpy.ndarray[float]): Current population function/fitness values. best_x (numpy.ndarray): Global best individual. best_fitness (float): Global best individual fitness/function value. **params (Dict[str, Any]): Additional arguments. Returns: Tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray, float, Dict[str, Any]]: 1. New population. 2. New population fitness/function values. 3. New global best solution. 4. New global best solutions fitness/objective value. 5. Additional arguments: * current_best (numpy.ndarray): Current generation's best individual. """ current_best = params.pop('current_best') elite = np.copy(population[:self.keep]) max_t = task.max_iters if not np.isinf(task.max_iters) else task.max_evals / self.population_size population = np.apply_along_axis(task.repair, 1, self.migration_operator(task.dimension, self.np1, self.np2, population)) population = np.apply_along_axis(task.repair, 1, self.adjusting_operator(task.iters, max_t, task.dimension, self.np1, self.np2, population, current_best)) population_fitness, population = self.evaluate_and_sort(task, population) current_best = population[0] population[-self.keep:] = elite population_fitness, population = self.evaluate_and_sort(task, population) best_x, best_fitness = self.get_best(population, population_fitness, best_x, best_fitness) return population, population_fitness, best_x, best_fitness, {'current_best': current_best}