Source code for NiaPy.algorithms.basic.mbo

# encoding=utf8
import logging

from numpy import argsort, sum, apply_along_axis, where, pi, ceil, isinf, array, copy, tan
from numpy.random import exponential

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 algorithmInfo(): r"""Get information of the algorithm. Returns: str: Algorithm information. See Also: * :func:`NiaPy.algorithms.algorithm.Algorithm.algorithmInfo` """ 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] @staticmethod def typeParameters(): r"""Get dictionary with functions for checking values of parameters. Returns: Dict[str, Callable]: * PAR (Callable[[float], bool]): Checks if partition parameter has a proper value. * PER (Callable[[float], bool]): Checks if period parameter has a proper value. See Also: * :func:`NiaPy.algorithms.algorithm.Algorithm.typeParameters` """ d = Algorithm.typeParameters() d.update({ 'PAR': lambda x: isinstance(x, float) and x > 0, 'PER': lambda x: isinstance(x, float) and x > 0 }) return d
[docs] def setParameters(self, NP=20, PAR=5.0 / 12.0, PER=1.2, **ukwargs): r"""Set the parameters of the algorithm. Args: NP (Optional[int]): Population size. PAR (Optional[int]): Partition. PER (Optional[int]): Period. ukwargs (Dict[str, Any]): Additional arguments. See Also: * :func:`NiaPy.algorithms.Algorithm.setParameters` """ Algorithm.setParameters(self, NP=NP, **ukwargs) self.NP, self.PAR, self.PER, self.keep, self.BAR, self.NP1 = NP, PAR, PER, 2, PAR, int(ceil(PAR * NP)) self.NP2 = int(NP - self.NP1)
[docs] def getParameters(self): r"""Get parameters values for the algorithm. Returns: Dict[str, Any]: TODO. """ d = Algorithm.getParameters(self) d.update({ 'PAR': self.PAR, 'PER': self.PER, 'keep': self.keep, 'BAR': self.BAR, 'NP1': self.NP1, 'NP2': self.NP2 }) return d
[docs] def repair(self, x, lower, upper): r"""Truncate exceeded dimensions to the limits. Args: x (numpy.ndarray): Individual to repair. lower (numpy.ndarray): Lower limits for dimensions. upper (numpy.ndarray): Upper limits for dimensions. Returns: numpy.ndarray: Repaired individual. """ ir = where(x < lower) x[ir] = lower[ir] ir = where(x > upper) x[ir] = upper[ir] return x
[docs] def levy(self, step_size, D): r"""Calculate levy flight. Args: step_size (float): Size of the walk step. D (int): Number of dimensions. Returns: numpy.ndarray: Calculated values for levy flight. """ delataX = array([sum(tan(pi * self.uniform(0.0, 1.0, 10))) for _ in range(0, D)]) return delataX
[docs] def migrationOperator(self, D, NP1, NP2, Butterflies): r"""Apply the migration operator. Args: D (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 = copy(Butterflies[:NP1]) pop2 = copy(Butterflies[NP1:]) for k1 in range(0, NP1): for parnum1 in range(0, D): r1 = self.uniform(0.0, 1.0) * self.PER if r1 <= self.PAR: r2 = self.randint(Nmin=0, Nmax=NP1 - 1) Butterflies[k1, parnum1] = pop1[r2, parnum1] else: r3 = self.randint(Nmin=0, Nmax=NP2 - 1) Butterflies[k1, parnum1] = pop2[r3, parnum1] return Butterflies
[docs] def adjustingOperator(self, t, max_t, D, NP1, NP2, Butterflies, best): r"""Apply the adjusting operator. Args: t (int): Current generation. max_t (int): Maximum generation. D (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 = copy(Butterflies[NP1:]) for k2 in range(NP1, NP1 + NP2): scale = 1.0 / ((t + 1)**2) step_size = ceil(exponential(2 * max_t)) delataX = self.levy(step_size, D) for parnum2 in range(0, D): if self.uniform(0.0, 1.0) >= self.PAR: Butterflies[k2, parnum2] = best[parnum2] else: r4 = self.randint(Nmin=0, Nmax=NP2 - 1) Butterflies[k2, parnum2] = pop2[r4, 1] if self.uniform(0.0, 1.0) > self.BAR: Butterflies[k2, parnum2] += scale * \ (delataX[parnum2] - 0.5) return Butterflies
[docs] def evaluateAndSort(self, 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 = apply_along_axis(task.eval, 1, Butterflies) indices = argsort(Fitness) Butterflies = Butterflies[indices] Fitness = Fitness[indices] return Fitness, Butterflies
[docs] def initPopulation(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: * dx (float): A small value used in local seeding stage. See Also: * :func:`NiaPy.algorithms.Algorithm.initPopulation` """ Butterflies = self.uniform(task.Lower, task.Upper, [self.NP, task.D]) Fitness, Butterflies = self.evaluateAndSort(task, Butterflies) return Butterflies, Fitness, {'tmp_best': Butterflies[0]}
[docs] def runIteration(self, task, Butterflies, Evaluations, xb, fxb, tmp_best, **dparams): r"""Core function of Forest Optimization Algorithm. Args: task (Task): Optimization task. Butterflies (numpy.ndarray): Current population. Evaluations (numpy.ndarray[float]): Current population function/fitness values. xb (numpy.ndarray): Global best individual. fxb (float): Global best individual fitness/function value. tmp_best (numpy.ndarray): Best individual currently. **dparams (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: * dx (float): A small value used in local seeding stage. """ tmpElite = copy(Butterflies[:self.keep]) max_t = task.nGEN if isinf(task.nGEN) is False else task.nFES / self.NP Butterflies = apply_along_axis(self.repair, 1, self.migrationOperator(task.D, self.NP1, self.NP2, Butterflies), task.Lower, task.Upper) Butterflies = apply_along_axis(self.repair, 1, self.adjustingOperator(task.Iters, max_t, task.D, self.NP1, self.NP2, Butterflies, tmp_best), task.Lower, task.Upper) Fitness, Butterflies = self.evaluateAndSort(task, Butterflies) tmp_best = Butterflies[0] Butterflies[-self.keep:] = tmpElite Fitness, Butterflies = self.evaluateAndSort(task, Butterflies) xb, fxb = self.getBest(Butterflies, Fitness, xb, fxb) return Butterflies, Fitness, xb, fxb, {'tmp_best': tmp_best}
# vim: tabstop=3 noexpandtab shiftwidth=3 softtabstop=3