Source code for mealpy.swarm_based.BA

#!/usr/bin/env python
# Created by "Thieu" at 12:00, 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 OriginalBA(Optimizer): """ The original version of: Bat-inspired Algorithm (BA) Notes ~~~~~ + The value of A and r parameters are constant Hyper-parameters should fine-tune in approximate range to get faster convergence toward the global optimum: + loudness (float): (1.0, 2.0), loudness, default = 0.8 + pulse_rate (float): (0.15, 0.85), pulse rate / emission rate, default = 0.95 + pulse_frequency (list, tuple): (pf_min, pf_max) -> ([0, 3], [5, 20]), pulse frequency, default = (0, 10) Examples ~~~~~~~~ >>> import numpy as np >>> from mealpy import FloatVar, BA >>> >>> 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 = BA.OriginalBA(epoch=1000, pop_size=50, loudness=0.8, pulse_rate=0.95, pf_min=0.1, pf_max=10.0) >>> 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] Yang, X.S., 2010. A new metaheuristic bat-inspired algorithm. In Nature inspired cooperative strategies for optimization (NICSO 2010) (pp. 65-74). Springer, Berlin, Heidelberg. """ def __init__(self, epoch: int = 10000, pop_size: int = 100, loudness: float = 0.8, pulse_rate: float = 0.95, pf_min: float = 0., pf_max: float = 10., **kwargs: object) -> None: """ Args: epoch (int): maximum number of iterations, default = 10000 pop_size (int): number of population size, default = 100 loudness (float): (A_min, A_max): loudness, default = 0.8 pulse_rate (float): (r_min, r_max): pulse rate / emission rate, default = 0.95 pf_min (float): pulse frequency min, default = 0 pf_max (float): pulse frequency max, default = 10 """ 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.loudness = self.validator.check_float("loudness", loudness, (0, 1.0)) self.pulse_rate = self.validator.check_float("pulse_rate", pulse_rate, (0, 1.0)) self.pf_min = self.validator.check_float("pf_min", pf_min, [0., 3.0]) self.pf_max = self.validator.check_float("pf_max", pf_max, [5., 20.]) self.set_parameters(["epoch", "pop_size", "loudness", "pulse_rate", "pf_min", "pf_max"]) self.alpha = self.gamma = 0.9 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) velocity = self.generator.uniform(self.problem.lb, self.problem.ub) pulse_frequency = self.pf_min + (self.pf_max - self.pf_min) * self.generator.uniform() return Agent(solution=solution, velocity=velocity, pulse_frequency=pulse_frequency)
[docs] def evolve(self, epoch): """ The main operations (equations) of algorithm. Inherit from Optimizer class Args: epoch (int): The current iteration """ pop_new = [] for idx in range(0, self.pop_size): agent = self.pop[idx].copy() vec = agent.velocity + self.pop[idx].pulse_frequency * (self.pop[idx].solution - self.g_best.solution) x_new = self.pop[idx].solution + agent.velocity ## Local Search around g_best position if self.generator.random() > self.pulse_rate: x_new = self.g_best.solution + 0.001 * self.generator.normal(self.problem.n_dims) pos_new = self.correct_solution(x_new) agent.update(solution=pos_new, velocity=vec) pop_new.append(agent) if self.mode not in self.AVAILABLE_MODES: pop_new[-1].target = self.get_target(pos_new) pop_new = self.update_target_for_population(pop_new) for idx in range(self.pop_size): ## Replace the old position by the new one when its has better fitness. ## and then update loudness and emission rate if self.compare_target(pop_new[idx].target, self.pop[idx].target, self.problem.minmax) and self.generator.random() < self.loudness: self.pop[idx].update(solution=pop_new[idx].solution, target=pop_new[idx].target)
[docs]class AdaptiveBA(Optimizer): """ The original version of: Adaptive Bat-inspired Algorithm (ABA) Notes ~~~~~ + The value of A and r are changing after each iteration Hyper-parameters should fine-tune in approximate range to get faster convergence toward the global optimum: + loudness_min (float): A_min - loudness, default=1.0 + loudness_max (float): A_max - loudness, default=2.0 + pr_min (float): pulse rate / emission rate min, default = 0.15 + pr_max (float): pulse rate / emission rate max, default = 0.85 + pf_min (float): pulse frequency min, default = 0 + pf_max (float): pulse frequency max, default = 10 Examples ~~~~~~~~ >>> import numpy as np >>> from mealpy import FloatVar, BA >>> >>> 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 = BA.AdaptiveBA(epoch=1000, pop_size=50, loudness_min = 1.0, loudness_max = 2.0, pr_min = -2.5, pr_max = 0.85, pf_min = 0.1, pf_max = 10.) >>> 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] Yang, X.S., 2010. A new metaheuristic bat-inspired algorithm. In Nature inspired cooperative strategies for optimization (NICSO 2010) (pp. 65-74). Springer, Berlin, Heidelberg. """ def __init__(self, epoch: int = 10000, pop_size: object = 100, loudness_min: float = 1.0, loudness_max: float = 2.0, pr_min: float = 0.15, pr_max: float = 0.85, pf_min: float = -10., pf_max: float = 10., **kwargs: object) -> None: """ Args: epoch (int): maximum number of iterations, default = 10000 pop_size (int): number of population size, default = 100 loudness_min (float): A_min - loudness, default=1.0 loudness_max (float): A_max - loudness, default=2.0 pr_min (float): pulse rate / emission rate min, default = 0.15 pr_max (float): pulse rate / emission rate max, default = 0.85 pf_min (float): pulse frequency min, default = 0 pf_max (float): pulse frequency max, default = 10 """ 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.loudness_min = self.validator.check_float("loudness_min", loudness_min, [0.5, 1.5]) self.loudness_max = self.validator.check_float("loudness_max", loudness_max, [1.5, 3.0]) self.pr_min = self.validator.check_float("pr_min", pr_min, (0, 1.0)) self.pr_max = self.validator.check_float("pr_max", pr_max, (0, 1.0)) self.pf_min = self.validator.check_float("pf_min", pf_min, [-10, 0]) self.pf_max = self.validator.check_float("pf_max", pf_max, [0, 10]) self.alpha = self.gamma = 0.9 self.set_parameters(["epoch", "pop_size", "loudness_min", "loudness_max", "pr_min", "pr_max", "pf_min", "pf_max"]) 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) velocity = self.generator.uniform(self.problem.lb, self.problem.ub) loudness = self.generator.uniform(self.loudness_min, self.loudness_max) pulse_rate = self.generator.uniform(self.pr_min, self.pr_max) return Agent(solution=solution, velocity=velocity, loudness=loudness, pulse_rate=pulse_rate)
[docs] def evolve(self, epoch): """ The main operations (equations) of algorithm. Inherit from Optimizer class Args: epoch (int): The current iteration """ mean_a = np.mean([agent.loudness for agent in self.pop]) pop_new = [] for idx in range(0, self.pop_size): agent = self.pop[idx].copy() pulse_frequency = self.generator.uniform(self.pf_min, self.pf_max) agent.velocity = agent.velocity + pulse_frequency * (self.pop[idx].solution - self.g_best.solution) x_new = self.pop[idx].solution + agent.velocity ## Local Search around g_best position if self.generator.random() > agent.pulse_rate: x_new = self.g_best.solution + mean_a * self.generator.normal(-1, 1) pos_new = self.correct_solution(x_new) agent.solution = pos_new pop_new.append(agent) if self.mode not in self.AVAILABLE_MODES: pop_new[-1].target = self.get_target(pos_new) pop_new = self.update_target_for_population(pop_new) for idx in range(0, self.pop_size): ## Replace the old position by the new one when its has better fitness. ## and then update loudness and emission rate if self.compare_target(pop_new[idx].target, self.pop[idx].target, self.problem.minmax) and self.generator.random() < pop_new[idx].loudness: loudness = self.alpha * pop_new[idx].loudness pulse_rate = pop_new[idx].pulse_rate * (1 - np.exp(-self.gamma * epoch)) self.pop[idx].update(solution=pop_new[idx].solution, target=pop_new[idx].target, loudness=loudness, pulse_rate=pulse_rate)
[docs]class DevBA(Optimizer): """ The original version of: Developed Bat-inspired Algorithm (DBA) Notes ~~~~~ + A (loudness) parameter is removed + Flow is changed: + 1st: the exploration phase is proceed (using frequency) + 2nd: If new position has better fitness, replace the old position + 3rd: Otherwise, proceed exploitation phase (using finding around the best position so far) Hyper-parameters should fine-tune in approximate range to get faster convergence toward the global optimum: + pulse_rate (float): [0.7, 1.0], pulse rate / emission rate, default = 0.95 + pulse_frequency (tuple, list): (pf_min, pf_max) -> ([0, 3], [5, 20]), pulse frequency, default = (0, 10) Examples ~~~~~~~~ >>> import numpy as np >>> from mealpy import FloatVar, BA >>> >>> 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 = BA.DevBA(epoch=1000, pop_size=50, pulse_rate = 0.95, pf_min = 0., pf_max = 10.) >>> 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}") """ def __init__(self, epoch=10000, pop_size=100, pulse_rate=0.95, pf_min=0., pf_max=10., **kwargs): 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.pulse_rate = self.validator.check_float("pulse_rate", pulse_rate, (0, 1.0)) self.pf_min = self.validator.check_float("pf_min", pf_min, [0, 2]) self.pf_max = self.validator.check_float("pf_max", pf_max, [2, 10]) self.alpha = self.gamma = 0.9 self.set_parameters(["epoch", "pop_size", "pulse_rate", "pf_min", "pf_max"]) self.sort_flag = False
[docs] def initialize_variables(self): self.dyn_list_velocity = np.zeros((self.pop_size, self.problem.n_dims))
[docs] def evolve(self, epoch): """ The main operations (equations) of algorithm. Inherit from Optimizer class Args: epoch (int): The current iteration """ pop_new = [] for idx in range(0, self.pop_size): pf = self.pf_min + (self.pf_max - self.pf_min) * self.generator.uniform() # Eq. 2 self.dyn_list_velocity[idx] = self.generator.uniform() * self.dyn_list_velocity[idx] + (self.g_best.solution - self.pop[idx].solution) * pf # Eq. 3 x = self.pop[idx].solution + self.dyn_list_velocity[idx] # Eq. 4 pos_new = self.correct_solution(x) agent = self.generate_empty_agent(pos_new) pop_new.append(agent) if self.mode not in self.AVAILABLE_MODES: pop_new[-1].target = self.get_target(pos_new) pop_new = self.update_target_for_population(pop_new) pop_child_idx = [] pop_child = [] for idx in range(0, self.pop_size): if self.compare_target(pop_new[idx].target, self.pop[idx].target, self.problem.minmax): self.pop[idx].update(solution=pop_new[idx].solution.copy(), target=pop_new[idx].target) else: if self.generator.random() > self.pulse_rate: x = self.g_best.solution + 0.01 * self.generator.uniform(self.problem.lb, self.problem.ub) pos_new = self.correct_solution(x) agent = self.generate_empty_agent(pos_new) pop_child_idx.append(idx) pop_child.append(agent) if self.mode not in self.AVAILABLE_MODES: pop_child[-1].target = self.get_target(pos_new) pop_child = self.update_target_for_population(pop_child) for idx, idx_selected in enumerate(pop_child_idx): if self.compare_target(pop_child[idx].target, pop_new[idx_selected].target, self.problem.minmax): pop_new[idx_selected].update(solution=pop_child[idx].solution, target=pop_child[idx].target) self.pop = pop_new