Source code for mealpy.swarm_based.BSA

#!/usr/bin/env python
# Created by "Thieu" at 11:59, 17/03/2020 ----------%
#       Email: nguyenthieu2102@gmail.com            %
#       Github: https://github.com/thieu1995        %
# --------------------------------------------------%

import numpy as np
from mealpy.optimizer import Optimizer
from mealpy.utils.agent import Agent


[docs]class OriginalBSA(Optimizer): """ The original version of: Bird Swarm Algorithm (BSA) Links: 1. https://doi.org/10.1080/0952813X.2015.1042530 2. https://www.mathworks.com/matlabcentral/fileexchange/51256-bird-swarm-algorithm-bsa Hyper-parameters should fine-tune in approximate range to get faster convergence toward the global optimum: + ff (int): (5, 20), flight frequency - default = 10 + pff (float): the probability of foraging for food - default = 0.8 + c_couples (list, tuple): [c1, c2] -> (2.0, 2.0), Cognitive accelerated coefficient, Social accelerated coefficient same as PSO + a_couples (list, tuple): [a1, a2] -> (1.5, 1.5), The indirect and direct effect on the birds' vigilance behaviours. + fc (float): (0.1, 1.0), The followed coefficient - default = 0.5 Examples ~~~~~~~~ >>> import numpy as np >>> from mealpy import FloatVar, BSA >>> >>> def objective_function(solution): >>> return np.sum(solution**2) >>> >>> problem_dict = { >>> "bounds": FloatVar(n_vars=30, lb=(-10.,) * 30, ub=(10.,) * 30, name="delta"), >>> "obj_func": objective_function, >>> "minmax": "min", >>> } >>> >>> model = BSA.OriginalBSA(epoch=1000, pop_size=50, ff = 10, pff = 0.8, c1 = 1.5, c2 = 1.5, a1 = 1.0, a2 = 1.0, fc = 0.5) >>> g_best = model.solve(problem_dict) >>> print(f"Solution: {g_best.solution}, Fitness: {g_best.target.fitness}") >>> print(f"Solution: {model.g_best.solution}, Fitness: {model.g_best.target.fitness}") References ~~~~~~~~~~ [1] Meng, X.B., Gao, X.Z., Lu, L., Liu, Y. and Zhang, H., 2016. A new bio-inspired optimisation algorithm: Bird Swarm Algorithm. Journal of Experimental & Theoretical Artificial Intelligence, 28(4), pp.673-687. """ def __init__(self, epoch: int = 10000, pop_size: int = 100, ff: int = 10, pff: float = 0.8, c1: float = 1.5, c2: float = 1.5, a1: float = 1.0, a2: float = 1.0, fc: float = 0.5, **kwargs: object) -> None: """ Args: epoch (int): maximum number of iterations, default = 10000 pop_size (int): number of population size, default = 100 ff (int): flight frequency - default = 10 pff (float): the probability of foraging for food - default = 0.8 c1 (float): Cognitive accelerated coefficient same as PSO c2 (float): Social accelerated coefficient same as PSO a1 (float): The indirect effect on the birds' vigilance behaviours. a2 (float): The direct effect on the birds' vigilance behaviours. fc (float): The followed coefficient - default = 0.5 """ super().__init__(**kwargs) self.epoch = self.validator.check_int("epoch", epoch, [1, 100000]) self.pop_size = self.validator.check_int("pop_size", pop_size, [5, 10000]) self.ff = self.validator.check_int("ff", ff, [2, int(self.pop_size/2)]) self.pff = self.validator.check_float("pff", pff, (0, 1.0)) self.c1 = self.validator.check_float("c1", c1, (0, 5.0)) self.c2 = self.validator.check_float("c2", c2, (0, 5.0)) self.a1 = self.validator.check_float("a1", a1, (0, 5.0)) self.a2 = self.validator.check_float("a2", a2, (0, 5.0)) self.fc = self.validator.check_float("fc", fc, (0, 1.0)) self.set_parameters(["epoch", "pop_size", "ff", "pff", "c1", "c2", "a1", "a2", "fc"]) self.sort_flag = False
[docs] def generate_empty_agent(self, solution: np.ndarray = None) -> Agent: if solution is None: solution = self.problem.generate_solution(encoded=True) local_position = solution.copy() return Agent(solution=solution, local_solution=local_position)
[docs] def generate_agent(self, solution: np.ndarray = None) -> Agent: agent = self.generate_empty_agent(solution) agent.target = self.get_target(agent.solution) agent.local_target = agent.target.copy() return agent
[docs] def evolve(self, epoch): """ The main operations (equations) of algorithm. Inherit from Optimizer class Args: epoch (int): The current iteration """ pos_list = np.array([agent.solution for agent in self.pop]) fit_list = np.array([agent.local_target.fitness for agent in self.pop]) pos_mean = np.mean(pos_list, axis=0) fit_sum = np.sum(fit_list) if epoch % self.ff != 0: pop_new = [] for idx in range(0, self.pop_size): agent = self.pop[idx].copy() prob = self.generator.uniform() * 0.2 + self.pff # The probability of foraging for food if self.generator.uniform() < prob: # Birds forage for food. Eq. 1 x_new = self.pop[idx].solution + self.c1 * \ self.generator.uniform() * (self.pop[idx].local_solution - self.pop[idx].solution) + \ self.c2 * self.generator.uniform() * (self.g_best.solution - self.pop[idx].solution) else: # Birds keep vigilance. Eq. 2 A1 = self.a1 * np.exp(-self.pop_size * self.pop[idx].local_target.fitness / (self.EPSILON + fit_sum)) k = self.generator.choice(list(set(range(0, self.pop_size)) - {idx})) t1 = (fit_list[idx] - fit_list[k]) / (abs(fit_list[idx] - fit_list[k]) + self.EPSILON) A2 = self.a2 * np.exp(t1 * self.pop_size * fit_list[k] / (fit_sum + self.EPSILON)) x_new = self.pop[idx].solution + A1 * self.generator.uniform(0, 1) * (pos_mean - self.pop[idx].solution) + \ A2 * self.generator.uniform(-1, 1) * (self.g_best.solution - self.pop[idx].solution) agent.solution = self.correct_solution(x_new) pop_new.append(agent) if self.mode not in self.AVAILABLE_MODES: agent.target = self.get_target(agent.solution) self.pop[idx] = self.get_better_agent(agent, self.pop[idx], self.problem.minmax) if self.mode in self.AVAILABLE_MODES: pop_new = self.update_target_for_population(pop_new) self.pop = self.greedy_selection_population(self.pop, pop_new, self.problem.minmax) else: pop_new = self.pop.copy() # Divide the bird swarm into two parts: producers and scroungers. min_idx = np.argmin(fit_list) max_idx = np.argmax(fit_list) choose = 0 if min_idx < 0.5 * self.pop_size and max_idx < 0.5 * self.pop_size: choose = 1 if min_idx > 0.5 * self.pop_size and max_idx < 0.5 * self.pop_size: choose = 2 if min_idx < 0.5 * self.pop_size and max_idx > 0.5 * self.pop_size: choose = 3 if min_idx > 0.5 * self.pop_size and max_idx > 0.5 * self.pop_size: choose = 4 if choose < 3: # Producing (Equation 5) for idx in range(int(self.pop_size / 2 + 1), self.pop_size): agent = self.pop[idx].copy() x_new = self.pop[idx].solution + self.generator.uniform(self.problem.lb, self.problem.ub) * self.pop[idx].solution agent.solution = self.correct_solution(x_new) pop_new[idx] = agent if choose == 1: x_new = self.pop[min_idx].solution + self.generator.uniform(self.problem.lb, self.problem.ub) * self.pop[min_idx].solution agent = self.pop[min_idx].copy() agent.solution = self.correct_solution(x_new) pop_new[min_idx] = agent for i in range(0, int(self.pop_size / 2)): if choose == 2 or min_idx != i: agent = self.pop[i].copy() FL = self.generator.uniform() * 0.4 + self.fc idx = self.generator.integers(0.5 * self.pop_size + 1, self.pop_size) x_new = self.pop[i].solution + (self.pop[idx].solution - self.pop[i].solution) * FL agent.solution = self.correct_solution(x_new) pop_new[i] = agent else: # Scrounging (Equation 6) for i in range(0, int(0.5 * self.pop_size)): agent = self.pop[i].copy() x_new = self.pop[i].solution + self.generator.uniform(self.problem.lb, self.problem.ub) * self.pop[i].solution agent.solution = self.correct_solution(x_new) pop_new[i] = agent if choose == 4: agent = self.pop[min_idx].copy() x_new = self.pop[min_idx].solution + self.generator.uniform(self.problem.lb, self.problem.ub) * self.pop[min_idx].solution agent.solution = self.correct_solution(x_new) for i in range(int(self.pop_size / 2 + 1), self.pop_size): if choose == 3 or min_idx != i: agent = self.pop[i].copy() FL = self.generator.uniform() * 0.4 + self.fc idx = self.generator.integers(0, 0.5 * self.pop_size) x_new = self.pop[i].solution + (self.pop[idx].solution - self.pop[i].solution) * FL agent.solution = self.correct_solution(x_new) pop_new[i] = agent if self.mode in self.AVAILABLE_MODES: pop_new = self.update_target_for_population(pop_new) else: for idx in range(0, self.pop_size): pop_new[idx].target = self.get_target(pop_new[idx].solution) self.pop = self.greedy_selection_population(self.pop, pop_new, self.problem.minmax)