Source code for mealpy.swarm_based.SLO

# !/usr/bin/env python
# Created by "Thieu" at 15:05, 03/06/2021 ----------%
#       Email: nguyenthieu2102@gmail.com            %
#       Github: https://github.com/thieu1995        %
# --------------------------------------------------%

import numpy as np
from math import gamma
from copy import deepcopy
from mealpy.optimizer import Optimizer


[docs]class BaseSLO(Optimizer): """ The original version of: Sea Lion Optimization Algorithm (SLO) Links: 1. https://www.researchgate.net/publication/333516932_Sea_Lion_Optimization_Algorithm 2. https://doi.org/10.14569/IJACSA.2019.0100548 Notes ~~~~~ + The original paper is unclear in some equations and parameters + This version is based on my expertise Examples ~~~~~~~~ >>> import numpy as np >>> from mealpy.swarm_based.SLO import BaseSLO >>> >>> def fitness_function(solution): >>> return np.sum(solution**2) >>> >>> problem_dict1 = { >>> "fit_func": fitness_function, >>> "lb": [-10, -15, -4, -2, -8], >>> "ub": [10, 15, 12, 8, 20], >>> "minmax": "min", >>> "verbose": True, >>> } >>> >>> epoch = 1000 >>> pop_size = 50 >>> model = BaseSLO(problem_dict1, epoch, pop_size) >>> best_position, best_fitness = model.solve() >>> print(f"Solution: {best_position}, Fitness: {best_fitness}") References ~~~~~~~~~~ [1] Masadeh, R., Mahafzah, B.A. and Sharieh, A., 2019. Sea lion optimization algorithm. Sea, 10(5), p.388. """ def __init__(self, problem, epoch=10000, pop_size=100, **kwargs): """ Args: problem (dict): The problem dictionary epoch (int): maximum number of iterations, default = 10000 pop_size (int): number of population size, default = 100 """ super().__init__(problem, kwargs) self.nfe_per_epoch = pop_size self.sort_flag = False self.epoch = epoch self.pop_size = pop_size
[docs] def amend_position(self, position=None): """ If solution out of bound at dimension x, then it will re-arrange to random location in the range of domain Args: position: vector position (location) of the solution. Returns: Amended position """ return np.where(np.logical_and(self.problem.lb <= position, position <= self.problem.ub), position, np.random.uniform(self.problem.lb, self.problem.ub))
[docs] def evolve(self, epoch): """ The main operations (equations) of algorithm. Inherit from Optimizer class Args: epoch (int): The current iteration """ c = 2 - 2 * epoch / self.epoch t0 = np.random.rand() v1 = np.sin(2 * np.pi * t0) v2 = np.sin(2 * np.pi * (1 - t0)) SP_leader = np.abs(v1 * (1 + v2) / v2) # In the paper this is not clear how to calculate pop_new = [] for idx in range(0, self.pop_size): if SP_leader < 0.25: if c < 1: pos_new = self.g_best[self.ID_POS] - c * np.abs(2 * np.random.rand() * self.g_best[self.ID_POS] - self.pop[idx][self.ID_POS]) else: ri = np.random.choice(list(set(range(0, self.pop_size)) - {idx})) # random index pos_new = self.pop[ri][self.ID_POS] - c * np.abs(2 * np.random.rand() * self.pop[ri][self.ID_POS] - self.pop[idx][self.ID_POS]) else: pos_new = np.abs(self.g_best[self.ID_POS] - self.pop[idx][self.ID_POS]) * \ np.cos(2 * np.pi * np.random.uniform(-1, 1)) + self.g_best[self.ID_POS] # In the paper doesn't check also doesn't update old solution at this point pos_new = self.amend_position(pos_new) pop_new.append([pos_new, None]) pop_new = self.update_fitness_population(pop_new) self.pop = self.greedy_selection_population(self.pop, pop_new)
[docs]class ModifiedSLO(Optimizer): """ My modified version of: Sea Lion Optimization (M-SLO) Notes ~~~~~ + Use the idea of shrink encircling combine with levy flight techniques + Beside, the idea of local best in PSO is used Examples ~~~~~~~~ >>> import numpy as np >>> from mealpy.swarm_based.SLO import ModifiedSLO >>> >>> def fitness_function(solution): >>> return np.sum(solution**2) >>> >>> problem_dict1 = { >>> "fit_func": fitness_function, >>> "lb": [-10, -15, -4, -2, -8], >>> "ub": [10, 15, 12, 8, 20], >>> "minmax": "min", >>> "verbose": True, >>> } >>> >>> epoch = 1000 >>> pop_size = 50 >>> model = ModifiedSLO(problem_dict1, epoch, pop_size) >>> best_position, best_fitness = model.solve() >>> print(f"Solution: {best_position}, Fitness: {best_fitness}") """ ID_LOC_POS = 2 ID_LOC_FIT = 3 def __init__(self, problem, epoch=10000, pop_size=100, **kwargs): """ Args: problem (dict): The problem dictionary epoch (int): maximum number of iterations, default = 10000 pop_size (int): number of population size, default = 100 """ super().__init__(problem, kwargs) self.nfe_per_epoch = pop_size self.sort_flag = False self.epoch = epoch self.pop_size = pop_size
[docs] def create_solution(self): """ To get the position, fitness wrapper, target and obj list + A[self.ID_POS] --> Return: position + A[self.ID_TAR] --> Return: [target, [obj1, obj2, ...]] + A[self.ID_TAR][self.ID_FIT] --> Return: target + A[self.ID_TAR][self.ID_OBJ] --> Return: [obj1, obj2, ...] Returns: list: wrapper of solution with format [position, [target, [obj1, obj2, ...]], local_pos, local_fit] """ ## Increase exploration at the first initial population using opposition-based learning. position = np.random.uniform(self.problem.lb, self.problem.ub) position = self.amend_position(position) fitness = self.get_fitness_position(position=position) local_pos = self.problem.lb + self.problem.ub - position local_pos = self.amend_position(local_pos) local_fit = self.get_fitness_position(local_pos) if fitness < local_fit: return [local_pos, local_fit, position, fitness] else: return [position, fitness, local_pos, local_fit]
def _shrink_encircling_levy__(self, current_pos, epoch, dist, c, beta=1): up = gamma(1 + beta) * np.sin(np.pi * beta / 2) down = (gamma((1 + beta) / 2) * beta * np.power(2, (beta - 1) / 2)) xich_ma_1 = np.power(up / down, 1 / beta) xich_ma_2 = 1 a = np.random.normal(0, xich_ma_1, 1) b = np.random.normal(0, xich_ma_2, 1) LB = 0.01 * a / (np.power(np.abs(b), 1 / beta)) * dist * c D = np.random.uniform(self.problem.lb, self.problem.ub) levy = LB * D return (current_pos - np.sqrt(epoch + 1) * np.sign(np.random.random(1) - 0.5)) * levy
[docs] def evolve(self, epoch): """ The main operations (equations) of algorithm. Inherit from Optimizer class Args: epoch (int): The current iteration """ c = 2 - 2 * epoch / self.epoch if c > 1: pa = 0.3 # At the beginning of the process, the probability for shrinking encircling is small else: pa = 0.7 # But at the end of the process, it become larger. Because sea lion are shrinking encircling prey SP_leader = np.random.uniform(0, 1) pop_new = [] for idx in range(0, self.pop_size): agent = deepcopy(self.pop[idx]) if SP_leader >= 0.6: pos_new = np.cos(2 * np.pi * np.random.normal(0, 1)) * \ np.abs(self.g_best[self.ID_POS] - self.pop[idx][self.ID_POS]) + self.g_best[self.ID_POS] else: if np.random.uniform() < pa: dist1 = np.random.uniform() * np.abs(2 * self.g_best[self.ID_POS] - self.pop[idx][self.ID_POS]) pos_new = self._shrink_encircling_levy__(self.pop[idx][self.ID_POS], epoch, dist1, c) else: rand_SL = self.pop[np.random.randint(0, self.pop_size)][self.ID_LOC_POS] rand_SL = 2 * self.g_best[self.ID_POS] - rand_SL pos_new = rand_SL - c * np.abs(np.random.uniform() * rand_SL - self.pop[idx][self.ID_POS]) agent[self.ID_POS] = self.amend_position(pos_new) pop_new.append(agent) pop_new = self.update_fitness_population(pop_new) for idx in range(0, self.pop_size): if self.compare_agent(pop_new[idx], self.pop[idx]): self.pop[idx] = deepcopy(pop_new[idx]) if self.compare_agent(pop_new[idx], [None, self.pop[idx][self.ID_LOC_FIT]]): self.pop[idx][self.ID_LOC_POS] = deepcopy(pop_new[idx][self.ID_POS]) self.pop[idx][self.ID_LOC_FIT] = deepcopy(pop_new[idx][self.ID_TAR])
[docs]class ISLO(ModifiedSLO): """ My improved version of: Improved Sea Lion Optimization (ISLO) Hyper-parameters should fine tuned in approximate range to get faster convergen toward the global optimum: + c1 (float): Local coefficient same as PSO, default = 1.2 + c2 (float): Global coefficient same as PSO, default = 1.2 Examples ~~~~~~~~ >>> import numpy as np >>> from mealpy.swarm_based.SLO import ISLO >>> >>> def fitness_function(solution): >>> return np.sum(solution**2) >>> >>> problem_dict1 = { >>> "fit_func": fitness_function, >>> "lb": [-10, -15, -4, -2, -8], >>> "ub": [10, 15, 12, 8, 20], >>> "minmax": "min", >>> "verbose": True, >>> } >>> >>> epoch = 1000 >>> pop_size = 50 >>> c1 = 1.2 >>> c2 = 1.5 >>> model = ISLO(problem_dict1, epoch, pop_size, c1, c2) >>> best_position, best_fitness = model.solve() >>> print(f"Solution: {best_position}, Fitness: {best_fitness}") """ def __init__(self, problem, epoch=10000, pop_size=100, c1=1.2, c2=1.2, **kwargs): """ Args: problem (dict): The problem dictionary epoch (int): maximum number of iterations, default = 10000 pop_size (int): number of population size, default = 100 c1 (float): Local coefficient same as PSO, default = 1.2 c2 (float): Global coefficient same as PSO, default = 1.2 """ super().__init__(problem, epoch, pop_size, **kwargs) self.nfe_per_epoch = pop_size self.sort_flag = False self.epoch = epoch self.pop_size = pop_size self.c1 = c1 self.c2 = c2
[docs] def evolve(self, epoch): """ The main operations (equations) of algorithm. Inherit from Optimizer class Args: epoch (int): The current iteration """ c = 2 - 2 * epoch / self.epoch t0 = np.random.rand() v1 = np.sin(2 * np.pi * t0) v2 = np.sin(2 * np.pi * (1 - t0)) SP_leader = np.abs(v1 * (1 + v2) / v2) pop_new = [] for idx in range(0, self.pop_size): agent = deepcopy(self.pop[idx]) if SP_leader < 0.5: if c < 1: # Exploitation improved by historical movement + global best affect # pos_new = g_best[self.ID_POS] - c * np.abs(2 * rand() * g_best[self.ID_POS] - pop[i][self.ID_POS]) dif1 = np.abs(2 * np.random.rand() * self.g_best[self.ID_POS] - self.pop[idx][self.ID_POS]) dif2 = np.abs(2 * np.random.rand() * self.pop[idx][self.ID_LOC_POS] - self.pop[idx][self.ID_POS]) pos_new = self.c1 * np.random.rand() * (self.pop[idx][self.ID_POS] - c * dif1) + \ self.c2 * np.random.rand() * (self.pop[idx][self.ID_POS] - c * dif2) else: # Exploration improved by opposition-based learning # Create a new solution by equation below # Then create an opposition solution of above solution # Compare both of them and keep the good one (Searching at both direction) pos_new = self.g_best[self.ID_POS] + c * np.random.normal(0, 1, self.problem.n_dims) * \ (self.g_best[self.ID_POS] - self.pop[idx][self.ID_POS]) fit_new = self.get_fitness_position(self.amend_position(pos_new)) pos_new_oppo = self.problem.lb + self.problem.ub - self.g_best[self.ID_POS] + \ np.random.rand() * (self.g_best[self.ID_POS] - pos_new) fit_new_oppo = self.get_fitness_position(self.amend_position(pos_new_oppo)) if self.compare_agent([pos_new_oppo, fit_new_oppo], [pos_new, fit_new]): pos_new = pos_new_oppo else: # Exploitation pos_new = self.g_best[self.ID_POS] + np.cos(2 * np.pi * np.random.uniform(-1, 1)) * \ np.abs(self.g_best[self.ID_POS] - self.pop[idx][self.ID_POS]) agent[self.ID_POS] = self.amend_position(pos_new) pop_new.append(agent) pop_new = self.update_fitness_population(pop_new) for idx in range(0, self.pop_size): if self.compare_agent(pop_new[idx], self.pop[idx]): self.pop[idx] = deepcopy(pop_new[idx]) if self.compare_agent(pop_new[idx], [None, self.pop[idx][self.ID_LOC_FIT]]): self.pop[idx][self.ID_LOC_POS] = deepcopy(pop_new[idx][self.ID_POS]) self.pop[idx][self.ID_LOC_FIT] = deepcopy(pop_new[idx][self.ID_TAR])