Source code for mealpy.swarm_based.GWO

#!/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.utils.chaotic import ChaoticMap as CM
from mealpy.utils.fuzzy import FuzzySystem as FS
from mealpy.optimizer import Optimizer


[docs]class OriginalGWO(Optimizer): """ The original version of: Grey Wolf Optimizer (GWO) Links: 1. https://doi.org/10.1016/j.advengsoft.2013.12.007 2. https://www.mathworks.com/matlabcentral/fileexchange/44974-grey-wolf-optimizer-gwo?s_tid=FX_rc3_behav Examples ~~~~~~~~ >>> import numpy as np >>> from mealpy import FloatVar, GWO >>> >>> def objective_function(solution): >>> return np.sum(solution**2) >>> >>> problem_dict = { >>> "bounds": FloatVar(lb=(-10.,) * 30, ub=(10.,) * 30, name="delta"), >>> "minmax": "min", >>> "obj_func": objective_function >>> } >>> >>> model = GWO.OriginalGWO(epoch=1000, pop_size=50) >>> 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] Mirjalili, S., Mirjalili, S.M. and Lewis, A., 2014. Grey wolf optimizer. Advances in engineering software, 69, pp.46-61. """ def __init__(self, epoch: int = 10000, pop_size: int = 100, **kwargs: object) -> None: """ Args: epoch (int): maximum number of iterations, default = 10000 pop_size (int): number of population size, default = 100 """ 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.set_parameters(["epoch", "pop_size"]) self.sort_flag = False
[docs] def evolve(self, epoch): """ The main operations (equations) of algorithm. Inherit from Optimizer class Args: epoch (int): The current iteration """ # linearly decreased from 2 to 0 a = 2 - 2. * epoch / self.epoch _, list_best, _ = self.get_special_agents(self.pop, n_best=3, minmax=self.problem.minmax) pop_new = [] for idx in range(0, self.pop_size): A1 = a * (2 * self.generator.random(self.problem.n_dims) - 1) A2 = a * (2 * self.generator.random(self.problem.n_dims) - 1) A3 = a * (2 * self.generator.random(self.problem.n_dims) - 1) C1 = 2 * self.generator.random(self.problem.n_dims) C2 = 2 * self.generator.random(self.problem.n_dims) C3 = 2 * self.generator.random(self.problem.n_dims) X1 = list_best[0].solution - A1 * np.abs(C1 * list_best[0].solution - self.pop[idx].solution) X2 = list_best[1].solution - A2 * np.abs(C2 * list_best[1].solution - self.pop[idx].solution) X3 = list_best[2].solution - A3 * np.abs(C3 * list_best[2].solution - self.pop[idx].solution) pos_new = (X1 + X2 + X3) / 3.0 pos_new = self.correct_solution(pos_new) agent = self.generate_empty_agent(pos_new) pop_new.append(agent) if self.mode not in self.AVAILABLE_MODES: agent.target = self.get_target(pos_new) 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)
[docs]class RW_GWO(Optimizer): """ The original version of: Random Walk Grey Wolf Optimizer (RW-GWO) Examples ~~~~~~~~ >>> import numpy as np >>> from mealpy import FloatVar, GWO >>> >>> def objective_function(solution): >>> return np.sum(solution**2) >>> >>> problem_dict = { >>> "bounds": FloatVar(lb=(-10.,) * 30, ub=(10.,) * 30, name="delta"), >>> "minmax": "min", >>> "obj_func": objective_function >>> } >>> >>> model = GWO.RW_GWO(epoch=1000, pop_size=50) >>> 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] Gupta, S. and Deep, K., 2019. A novel random walk grey wolf optimizer. Swarm and evolutionary computation, 44, pp.101-112. """ def __init__(self, epoch: int = 10000, pop_size: int = 100, **kwargs: object) -> None: """ Args: epoch (int): maximum number of iterations, default = 10000 pop_size (int): number of population size, default = 100 """ 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.set_parameters(["epoch", "pop_size"]) self.sort_flag = False
[docs] def evolve(self, epoch): """ The main operations (equations) of algorithm. Inherit from Optimizer class Args: epoch (int): The current iteration """ # linearly decreased from 2 to 0, Eq. 5 b = 2. - 2. * epoch / self.epoch # linearly decreased from 2 to 0 a = 2. - 2. * epoch / self.epoch _, leaders, _ = self.get_special_agents(self.pop, n_best=3, minmax=self.problem.minmax) ## Random walk here leaders_new = [] for idx in range(0, len(leaders)): pos_new = leaders[idx].solution + a * self.generator.standard_cauchy(self.problem.n_dims) pos_new = self.correct_solution(pos_new) agent = self.generate_empty_agent(pos_new) leaders_new.append(agent) if self.mode not in self.AVAILABLE_MODES: agent.target = self.get_target(pos_new) leaders[idx] = self.get_better_agent(agent, leaders[idx], self.problem.minmax) if self.mode in self.AVAILABLE_MODES: leaders_new = self.update_target_for_population(leaders_new) leaders = self.greedy_selection_population(leaders, leaders_new, self.problem.minmax) ## Update other wolfs pop_new = [] for idx in range(0, self.pop_size): # Eq. 3 and 4 miu1 = a * (2 * self.generator.random(self.problem.n_dims) - 1) miu2 = a * (2 * self.generator.random(self.problem.n_dims) - 1) miu3 = a * (2 * self.generator.random(self.problem.n_dims) - 1) c1 = 2 * self.generator.random(self.problem.n_dims) c2 = 2 * self.generator.random(self.problem.n_dims) c3 = 2 * self.generator.random(self.problem.n_dims) X1 = leaders[0].solution - miu1 * np.abs(c1 * self.g_best.solution - self.pop[idx].solution) X2 = leaders[1].solution - miu2 * np.abs(c2 * self.g_best.solution - self.pop[idx].solution) X3 = leaders[2].solution - miu3 * np.abs(c3 * self.g_best.solution - self.pop[idx].solution) pos_new = (X1 + X2 + X3) / 3.0 pos_new = self.correct_solution(pos_new) agent = self.generate_empty_agent(pos_new) pop_new.append(agent) if self.mode not in self.AVAILABLE_MODES: agent.target = self.get_target(pos_new) 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) self.pop = self.get_sorted_and_trimmed_population(self.pop + leaders, self.pop_size, self.problem.minmax)
[docs]class GWO_WOA(OriginalGWO): """ The original version of: Hybrid Grey Wolf - Whale Optimization Algorithm (GWO-WOA) Links: 1. https://sci-hub.se/https://doi.org/10.1177/10775463211003402 Examples ~~~~~~~~ >>> import numpy as np >>> from mealpy import FloatVar, GWO >>> >>> def objective_function(solution): >>> return np.sum(solution**2) >>> >>> problem_dict = { >>> "bounds": FloatVar(lb=(-10.,) * 30, ub=(10.,) * 30, name="delta"), >>> "minmax": "min", >>> "obj_func": objective_function >>> } >>> >>> model = GWO.GWO_WOA(epoch=1000, pop_size=50) >>> 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] Obadina, O. O., Thaha, M. A., Althoefer, K., & Shaheed, M. H. (2022). Dynamic characterization of a master–slave robotic manipulator using a hybrid grey wolf–whale optimization algorithm. Journal of Vibration and Control, 28(15-16), 1992-2003. """ def __init__(self, epoch: int = 10000, pop_size: int = 100, **kwargs: object) -> None: """ Args: epoch (int): maximum number of iterations, default = 10000 pop_size (int): number of population size, default = 100 """ super().__init__(epoch, pop_size, **kwargs) self.bb = 1.0 self.sort_flag = False
[docs] def evolve(self, epoch): """ The main operations (equations) of algorithm. Inherit from Optimizer class Args: epoch (int): The current iteration """ # linearly decreased from 2 to 0 a = 2. - epoch / self.epoch _, list_best, _ = self.get_special_agents(self.pop, n_best=3, minmax=self.problem.minmax) pop_new = [] for idx in range(0, self.pop_size): A1 = a * (2 * self.generator.random(self.problem.n_dims) - 1) A2 = a * (2 * self.generator.random(self.problem.n_dims) - 1) A3 = a * (2 * self.generator.random(self.problem.n_dims) - 1) C1 = 2 * self.generator.random(self.problem.n_dims) C2 = 2 * self.generator.random(self.problem.n_dims) C3 = 2 * self.generator.random(self.problem.n_dims) if self.generator.random() < 0.5: da = self.generator.random() * np.abs(C1 * list_best[0].solution - self.pop[idx].solution) else: P, L = self.generator.random(), self.generator.uniform(-1, 1) da = P * np.exp(self.bb * L) * np.cos(2*np.pi*L) * np.abs(C1 * list_best[0].solution - self.pop[idx].solution) X1 = list_best[0].solution - A1 * da X2 = list_best[1].solution - A2 * np.abs(C2 * list_best[1].solution - self.pop[idx].solution) X3 = list_best[2].solution - A3 * np.abs(C3 * list_best[2].solution - self.pop[idx].solution) pos_new = (X1 + X2 + X3) / 3.0 pos_new = self.correct_solution(pos_new) agent = self.generate_empty_agent(pos_new) pop_new.append(agent) if self.mode not in self.AVAILABLE_MODES: agent.target = self.get_target(pos_new) 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)
[docs]class IGWO(OriginalGWO): """ The original version of: Improved Grey Wolf Optimization (IGWO) Notes: 1. Link: https://doi.org/10.1007/s00366-017-0567-1 2. Inspired by: Mohammadtaher Abbasi (https://github.com/mtabbasi) Hyper-parameters should fine-tune in approximate range to get faster convergence toward the global optimum: + a_min (float): Lower bound of a, default = 0.02 + a_max (float): Upper bound of a, default = 2.2 Examples ~~~~~~~~ >>> import numpy as np >>> from mealpy import FloatVar, GWO >>> >>> def objective_function(solution): >>> return np.sum(solution**2) >>> >>> problem_dict = { >>> "bounds": FloatVar(lb=(-10.,) * 30, ub=(10.,) * 30, name="delta"), >>> "minmax": "min", >>> "obj_func": objective_function >>> } >>> >>> model = GWO.IGWO(epoch=1000, pop_size=50, a_min = 0.02, a_max = 2.2) >>> 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] Kaveh, A. & Zakian, P.. (2018). Improved GWO algorithm for optimal design of truss structures. Engineering with Computers. 34. 10.1007/s00366-017-0567-1. """ def __init__(self, epoch: int = 10000, pop_size: int = 100, a_min: float = 0.02, a_max: float = 2.2, **kwargs: object) -> None: """ Args: epoch (int): maximum number of iterations, default = 10000 pop_size (int): number of population size, default = 100 a_min (float): Lower bound of a, default = 0.02 a_max (float): Upper bound of a, default = 2.2 """ super().__init__(epoch, pop_size, **kwargs) self.a_min = self.validator.check_float("a_min", a_min, (0.0, 1.6)) self.a_max = self.validator.check_float("a_max", a_max, [1., 4.]) self.set_parameters(["epoch", "pop_size", "a_min", "a_max"]) self.growth_alpha = 2 self.growth_delta = 3
[docs] def evolve(self, epoch): """ The main operations (equations) of algorithm. Args: epoch (int): The current iteration """ _, list_best, _ = self.get_special_agents(self.pop, n_best=3, minmax=self.problem.minmax) pop_new = [] for idx in range(0, self.pop_size): # IGWO functions a_alpha = self.a_max * np.exp((epoch / self.epoch) ** self.growth_alpha * np.log(self.a_min / self.a_max)) a_delta = self.a_max * np.exp((epoch / self.epoch) ** self.growth_delta * np.log(self.a_min / self.a_max)) a_beta = (a_alpha + a_delta) * 0.5 A1 = a_alpha * (2 * self.generator.random(self.problem.n_dims) - 1) A2 = a_beta * (2 * self.generator.random(self.problem.n_dims) - 1) A3 = a_delta * (2 * self.generator.random(self.problem.n_dims) - 1) C1 = 2 * self.generator.random(self.problem.n_dims) C2 = 2 * self.generator.random(self.problem.n_dims) C3 = 2 * self.generator.random(self.problem.n_dims) X1 = list_best[0].solution - A1 * np.abs(C1 * list_best[0].solution - self.pop[idx].solution) X2 = list_best[1].solution - A2 * np.abs(C2 * list_best[1].solution - self.pop[idx].solution) X3 = list_best[2].solution - A3 * np.abs(C3 * list_best[2].solution - self.pop[idx].solution) pos_new = (X1 + X2 + X3) / 3.0 pos_new = self.correct_solution(pos_new) agent = self.generate_empty_agent(pos_new) pop_new.append(agent) if self.mode not in self.AVAILABLE_MODES: agent.target = self.get_target(pos_new) 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)
[docs]class ChaoticGWO(Optimizer): """ The original version of: Chaotic-based Grey Wolf Optimizer (Chaotic-GWO or C-GWO) Links: 1. https://doi.org/10.1016/j.jcde.2017.02.005 Examples ~~~~~~~~ >>> import numpy as np >>> from mealpy import FloatVar, GWO >>> >>> def objective_function(solution): >>> return np.sum(solution**2) >>> >>> problem_dict = { >>> "bounds": FloatVar(lb=(-10.,) * 30, ub=(10.,) * 30, name="delta"), >>> "minmax": "min", >>> "obj_func": objective_function >>> } >>> >>> model = GWO.ChaoticGWO(epoch=1000, pop_size=50, chaotic_name="chebyshev", initial_chaotic_value=0.7) >>> 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] Kohli, M., & Arora, S. (2018). Chaotic grey wolf optimization algorithm for constrained optimization problems. Journal of computational design and engineering, 5(4), 458-472. """ CHAOTIC_MAPS = { "bernoulli": CM.bernoulli_map, "logistic": CM.logistic_map, "chebyshev": CM.chebyshev_map, "circle": CM.circle_map, "cubic": CM.cubic_map, "icmic": CM.icmic_map, "piecewise": CM.piecewise_map, "singer": CM.singer_map, "sinusoidal": CM.sinusoidal_map, "tent": CM.tent_map } def __init__(self, epoch: int = 10000, pop_size: int = 100, chaotic_name: str = "chebyshev", initial_chaotic_value: float = 0.7, **kwargs: object) -> None: """ Args: epoch (int): maximum number of iterations, default = 10000 pop_size (int): number of population size, default = 100 chaotic_name (str): name of chaotic map to use, default = "chebyshev" initial_chaotic_value (float): initial value for chaotic map, default = 0.7 """ 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.chaotic_name = self.validator.check_str("chaotic_name", chaotic_name, ChaoticGWO.CHAOTIC_MAPS.keys()) self.initial_chaotic_value = self.validator.check_float("initial_chaotic_value", initial_chaotic_value, [0.0, 1.0]) self.set_parameters(["epoch", "pop_size", "chaotic_name", "initial_chaotic_value"]) self.sort_flag = False
[docs] def initialize_variables(self) -> None: self.chao_value = self.initial_chaotic_value self.chao_func = ChaoticGWO.CHAOTIC_MAPS[self.chaotic_name]
def _update_chao_value(self): """Update chaotic value using selected chaotic map""" chao_value = self.chao_func(self.chao_value) # Ensure chaotic value stays in [0, 1] self.chao_value = np.clip(chao_value, 0, 1)
[docs] def evolve(self, epoch): """ The main operations (equations) of algorithm. Inherit from Optimizer class Args: epoch (int): The current iteration """ # linearly decreased from 2 to 0 a = 2 - 2. * epoch / self.epoch _, list_best, _ = self.get_special_agents(self.pop, n_best=3, minmax=self.problem.minmax) pop_new = [] for idx in range(0, self.pop_size): self._update_chao_value() A1 = a * (2 * self.generator.random(self.problem.n_dims) * self.chao_value - 1) A2 = a * (2 * self.generator.random(self.problem.n_dims) * self.chao_value - 1) A3 = a * (2 * self.generator.random(self.problem.n_dims) * self.chao_value - 1) C1 = 2 * self.generator.random(self.problem.n_dims) * self.chao_value C2 = 2 * self.generator.random(self.problem.n_dims) * self.chao_value C3 = 2 * self.generator.random(self.problem.n_dims) * self.chao_value X1 = list_best[0].solution - A1 * np.abs(C1 * list_best[0].solution - self.pop[idx].solution) X2 = list_best[1].solution - A2 * np.abs(C2 * list_best[1].solution - self.pop[idx].solution) X3 = list_best[2].solution - A3 * np.abs(C3 * list_best[2].solution - self.pop[idx].solution) pos_new = (X1 + X2 + X3) / 3.0 pos_new = self.correct_solution(pos_new) agent = self.generate_empty_agent(pos_new) pop_new.append(agent) if self.mode not in self.AVAILABLE_MODES: agent.target = self.get_target(pos_new) 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)
[docs]class FuzzyGWO(Optimizer): """ The original version of: Fuzzy Hierarchical Operator - Grey Wolf Optimizer (FHO-GWO or FuzzyGWO or F-GWO) Links: 1. https://doi.org/10.1016/j.asoc.2017.03.048 Examples ~~~~~~~~ >>> import numpy as np >>> from mealpy import FloatVar, GWO >>> >>> def objective_function(solution): >>> return np.sum(solution**2) >>> >>> problem_dict = { >>> "bounds": FloatVar(lb=(-10.,) * 30, ub=(10.,) * 30, name="delta"), >>> "minmax": "min", >>> "obj_func": objective_function >>> } >>> >>> model = GWO.FuzzyGWO(epoch=1000, pop_size=50, fuzzy_name="increase") >>> 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] Rodríguez, Luis, Oscar Castillo, José Soria, Patricia Melin, Fevrier Valdez, Claudia I. Gonzalez, Gabriela E. Martinez, and Jesus Soto. "A fuzzy hierarchical operator in the grey wolf optimizer algorithm." Applied Soft Computing 57 (2017): 315-328. """ FUZZY_OPERATORS = ["increase", "decrease"] def __init__(self, epoch: int = 10000, pop_size: int = 100, fuzzy_name: str = "increase", **kwargs: object) -> None: """ Args: epoch (int): maximum number of iterations, default = 10000 pop_size (int): number of population size, default = 100 fuzzy_name (str): type of fuzzy operator to use, default = "increase" """ 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.fuzzy_name = self.validator.check_str("fuzzy_name", fuzzy_name, FuzzyGWO.FUZZY_OPERATORS) self.set_parameters(["epoch", "pop_size", "fuzzy_name"]) self.sort_flag = False
[docs] def initialize_variables(self) -> None: self.fuzzy_system = FS(self.fuzzy_name)
[docs] def evolve(self, epoch): """ The main operations (equations) of algorithm. Inherit from Optimizer class Args: epoch (int): The current iteration """ # linearly decreased from 2 to 0 a = 2 - 2. * epoch / self.epoch _, list_best, _ = self.get_special_agents(self.pop, n_best=3, minmax=self.problem.minmax) pop_new = [] for idx in range(0, self.pop_size): A1 = a * (2 * self.generator.random(self.problem.n_dims) - 1) A2 = a * (2 * self.generator.random(self.problem.n_dims) - 1) A3 = a * (2 * self.generator.random(self.problem.n_dims) - 1) C1 = 2 * self.generator.random(self.problem.n_dims) C2 = 2 * self.generator.random(self.problem.n_dims) C3 = 2 * self.generator.random(self.problem.n_dims) X1 = list_best[0].solution - A1 * np.abs(C1 * list_best[0].solution - self.pop[idx].solution) X2 = list_best[1].solution - A2 * np.abs(C2 * list_best[1].solution - self.pop[idx].solution) X3 = list_best[2].solution - A3 * np.abs(C3 * list_best[2].solution - self.pop[idx].solution) # Get fuzzy weights FW_alpha, FW_beta, FW_delta = self.fuzzy_system.get_fuzzy_weights(epoch, self.epoch) total_weight = FW_alpha + FW_beta + FW_delta pos_new = (X1 * FW_alpha + X2 * FW_beta + X3 * FW_delta) / total_weight pos_new = self.correct_solution(pos_new) agent = self.generate_empty_agent(pos_new) pop_new.append(agent) if self.mode not in self.AVAILABLE_MODES: agent.target = self.get_target(pos_new) 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)
[docs]class IncrementalGWO(Optimizer): """ The original version of: Incremental model-based Grey Wolf Optimizer (IncrementalGWO) Notes: + When calling the solve() function, you need to set the mode to "swarm" to use this algorithm as original version. + They update the position of whole population before calculating the fitness of each agent. Links: 1. https://doi.org/10.1007/s00366-019-00837-7 Examples ~~~~~~~~ >>> import numpy as np >>> from mealpy import FloatVar, GWO >>> >>> def objective_function(solution): >>> return np.sum(solution**2) >>> >>> problem_dict = { >>> "bounds": FloatVar(lb=(-10.,) * 30, ub=(10.,) * 30, name="delta"), >>> "minmax": "min", >>> "obj_func": objective_function >>> } >>> >>> model = GWO.IncrementalGWO(epoch=1000, pop_size=50, explore_factor=1.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] Seyyedabbasi, A., & Kiani, F. (2021). I-GWO and Ex-GWO: improved algorithms of the Grey Wolf Optimizer to solve global optimization problems. Engineering with Computers, 37(1), 509-532. """ def __init__(self, epoch: int = 10000, pop_size: int = 100, explore_factor: float = 1.5, **kwargs: object) -> None: """ Args: epoch (int): maximum number of iterations, default = 10000 pop_size (int): number of population size, default = 100 explore_factor (float): factor to control exploration, default = 1.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.explore_factor = self.validator.check_float("explore_factor", explore_factor, [0.0, 5.0]) self.set_parameters(["epoch", "pop_size", "explore_factor"]) self.sort_flag = False
[docs] def evolve(self, epoch): """ The main operations (equations) of algorithm. Inherit from Optimizer class Args: epoch (int): The current iteration """ # linearly decreased from 2 to 0 a = 2 * (1. - (epoch / self.epoch)**self.explore_factor) pop_sorted, list_best, _ = self.get_special_agents(self.pop, n_best=3, minmax=self.problem.minmax) pop_new = [] for idx in range(0, self.pop_size): if idx == 0: # Alpha wolf updates based on hunting mechanism A = a * (2 * self.generator.random(self.problem.n_dims) - 1) C = 2 * self.generator.random(self.problem.n_dims) pos_new = list_best[0].solution - A * np.abs(C * list_best[0].solution - self.pop[idx].solution) else: # Other wolves update based on all previous wolves (Equation 19) # Average position of all previous wolves (n-1 wolves) p_temp = np.array([agent.solution for agent in pop_sorted]) mask = np.arange(p_temp.shape[0]) != idx pos_new = p_temp[mask].mean(axis=0) pos_new = self.correct_solution(pos_new) agent = self.generate_empty_agent(pos_new) pop_new.append(agent) if self.mode not in self.AVAILABLE_MODES: agent.target = self.get_target(pos_new) 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)
[docs]class ExGWO(Optimizer): """ The original version of: Expanded Grey Wolf Optimizer (Ex-GWO) Notes: + When calling the solve() function, you need to set the mode to "swarm" to use this algorithm as original version. + They update the position of whole population before calculating the fitness of each agent. Links: 1. https://doi.org/10.1007/s00366-019-00837-7 Examples ~~~~~~~~ >>> import numpy as np >>> from mealpy import FloatVar, GWO >>> >>> def objective_function(solution): >>> return np.sum(solution**2) >>> >>> problem_dict = { >>> "bounds": FloatVar(lb=(-10.,) * 30, ub=(10.,) * 30, name="delta"), >>> "minmax": "min", >>> "obj_func": objective_function >>> } >>> >>> model = GWO.ExGWO(epoch=1000, pop_size=50) >>> 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] Seyyedabbasi, A., & Kiani, F. (2021). I-GWO and Ex-GWO: improved algorithms of the Grey Wolf Optimizer to solve global optimization problems. Engineering with Computers, 37(1), 509-532. """ def __init__(self, epoch: int = 10000, pop_size: int = 100, **kwargs: object) -> None: """ Args: epoch (int): maximum number of iterations, default = 10000 pop_size (int): number of population size, default = 100 """ 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.set_parameters(["epoch", "pop_size"]) self.sort_flag = False
[docs] def evolve(self, epoch): """ The main operations (equations) of algorithm. Inherit from Optimizer class Args: epoch (int): The current iteration """ # linearly decreased from 2 to 0 a = 2 * (1. - epoch / self.epoch) pop_sorted, list_best, _ = self.get_special_agents(self.pop, n_best=3, minmax=self.problem.minmax) pop_new = [] for idx in range(0, self.pop_size): if idx == 0: A1 = a * (2 * self.generator.random(self.problem.n_dims) - 1) C1 = 2 * self.generator.random(self.problem.n_dims) pos_new = list_best[0].solution - A1 * np.abs(C1 * list_best[0].solution - self.pop[idx].solution) elif idx == 1: A2 = a * (2 * self.generator.random(self.problem.n_dims) - 1) C2 = 2 * self.generator.random(self.problem.n_dims) pos_new = list_best[1].solution - A2 * np.abs(C2 * list_best[1].solution - self.pop[idx].solution) elif idx == 2: A3 = a * (2 * self.generator.random(self.problem.n_dims) - 1) C3 = 2 * self.generator.random(self.problem.n_dims) pos_new = list_best[2].solution - A3 * np.abs(C3 * list_best[2].solution - self.pop[idx].solution) else: # Other wolves update based on first three + previous wolves (Equation 15) # Average of first three wolves + previously updated wolves pos_new = np.mean([agent.solution for agent in pop_sorted[:idx]], axis=0) pos_new = self.correct_solution(pos_new) agent = self.generate_empty_agent(pos_new) pop_new.append(agent) if self.mode not in self.AVAILABLE_MODES: agent.target = self.get_target(pos_new) 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)
[docs]class DS_GWO(Optimizer): """ The original version of: Diversity enhanced Strategy based Grey Wolf Optimizer (DS-GWO) This implementation includes: 1. Group-stage competition mechanism 2. Exploration-exploitation balance mechanism Links: 1. https://doi.org/10.1016/j.knosys.2022.109100 Examples ~~~~~~~~ >>> import numpy as np >>> from mealpy import FloatVar, GWO >>> >>> def objective_function(solution): >>> return np.sum(solution**2) >>> >>> problem_dict = { >>> "bounds": FloatVar(lb=(-10.,) * 30, ub=(10.,) * 30, name="delta"), >>> "minmax": "min", >>> "obj_func": objective_function >>> } >>> >>> model = GWO.DS_GWO(epoch=1000, pop_size=50, explore_ratio=0.4, n_groups=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] Jiang, Jianhua, Ziying Zhao, Yutong Liu, Weihua Li, and Huan Wang. "DSGWO: An improved grey wolf optimizer with diversity enhanced strategy based on group-stage competition and balance mechanisms." Knowledge-Based Systems 250 (2022): 109100. """ def __init__(self, epoch: int = 10000, pop_size: int = 100, explore_ratio: float = 0.4, n_groups: int = 5, **kwargs: object) -> None: """ Args: epoch (int): maximum number of iterations, default = 10000 pop_size (int): number of population size, default = 100 explore_ratio (float): ratio to control exploration, default = 0.4 n_groups (int): number of groups for group-stage competition, default = 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, [10, 10000]) self.explore_ratio = self.validator.check_float("explore_ratio", explore_ratio, [0.0, 1.0]) self.n_groups = self.validator.check_int("n_groups", n_groups, [5, 100]) self.set_parameters(["epoch", "pop_size", "explore_ratio", "n_groups"]) self.sort_flag = False
[docs] def initialize_variables(self): """ Initialize any variables needed for the algorithm. """ self.explore_epoch = int(self.epoch * self.explore_ratio)
[docs] def before_main_loop(self): """ Initialize variables before the main loop starts. """ self.group_stage_competition()
[docs] def get_coefficients(self, a: float) -> tuple: """ Generate coefficients A and C for position update equations. Args: a (float): Coefficient that decreases over epochs Returns: tuple: Coefficients A, C """ A = a * (2 * self.generator.random(self.problem.n_dims) - 1) C = 2 * self.generator.random(self.problem.n_dims) return A, C
[docs] def group_stage_competition(self): """ Group-stage competition mechanism: 1. Divide population into 6 subgroups 2. Select best wolf from each subgroup as delta candidates 3. Set best overall as alpha 4. Set delta candidate farthest from alpha as beta """ # Divide population into n_groups group_size = self.pop_size // self.n_groups self.delta_candidates = [] for idx in range(self.n_groups): start_idx = idx * group_size if idx == self.n_groups - 1: # Last group takes remaining wolves end_idx = self.pop_size else: end_idx = (idx + 1) * group_size # Get group members group_population = self.pop[start_idx:end_idx] # Find best wolf in group group_sorted = self.get_sorted_population(group_population, minmax=self.problem.minmax) self.delta_candidates.append(group_sorted[0].copy()) # Set alpha wolf (best among all delta candidates) _, list_best, _ = self.get_special_agents(self.delta_candidates, n_best=1, minmax=self.problem.minmax) self.alpha = list_best[0].copy() # Set beta wolf (delta candidate farthest from alpha) delta_pos = np.array([agent.solution for agent in self.delta_candidates]) distances = np.linalg.norm(delta_pos - self.alpha.solution, axis=1) beta_idx = np.argmax(distances) self.beta = self.delta_candidates[beta_idx].copy()
[docs]class IOBL_GWO(Optimizer): """ The original version of: Improved Opposite-based Learning Grey Wolf Optimizer (IOBL-GWO) Notes: + In the paper, they called it "Improved Grey Wolf Optimizer (IGWO)", but there are many improved versions of GWO. + So based on their proposed equations, we called it as "Improved Opposite-based Learning Grey Wolf Optimizer (IOBL-GWO)". + This algorithm is heavily (4x - 6X slower than original) because of multiple times of calculating the fitness of agent in each population. Links: 1. https://doi.org/10.1007/s12652-020-02153-1 Examples ~~~~~~~~ >>> import numpy as np >>> from mealpy import FloatVar, GWO >>> >>> def objective_function(solution): >>> return np.sum(solution**2) >>> >>> problem_dict = { >>> "bounds": FloatVar(lb=(-10.,) * 30, ub=(10.,) * 30, name="delta"), >>> "minmax": "min", >>> "obj_func": objective_function >>> } >>> >>> model = GWO.IOBL_GWO(epoch=1000, pop_size=50) >>> 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] Bansal, J. C., & Singh, S. (2021). A better exploration strategy in Grey Wolf Optimizer. Journal of Ambient Intelligence and Humanized Computing, 12(1), 1099-1118. """ def __init__(self, epoch: int = 10000, pop_size: int = 100, **kwargs: object) -> None: """ Args: epoch (int): maximum number of iterations, default = 10000 pop_size (int): number of population size, default = 100 """ 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.set_parameters(["epoch", "pop_size"]) self.is_parallelizable = False self.sort_flag = False
[docs] def evolve(self, epoch): """ The main operations (equations) of algorithm. Inherit from Optimizer class Args: epoch (int): The current iteration """ # linearly decreased from 2 to 0 a = 2 - 2. * epoch / self.epoch _, list_best, _ = self.get_special_agents(self.pop, n_best=3, minmax=self.problem.minmax) for idx in range(0, self.pop_size): # Try explorative equation first r1, r2, r3, r4, r5 = self.generator.random(5) if r5 >= 0.5: # Exploration around random wolf # Select random wolf from population jdx = self.generator.choice(list(set(range(self.pop_size)) - {idx})) x_rand = self.pop[jdx].solution pos_new = x_rand - r1 * np.abs(x_rand - 2 * r2 * self.pop[idx].solution) else: # Exploration around alpha wolf # Calculate average position of all wolves x_avg = np.mean([agent.solution for agent in self.pop], axis=0) pos_new = (list_best[0].solution - x_avg) - r3 * (self.problem.lb + r4 * (self.problem.ub - self.problem.lb)) # Apply boundary constraints pos_new = self.correct_solution(pos_new) tar_new = self.get_target(pos_new) if self.compare_target(tar_new, self.pop[idx].target, self.problem.minmax): # If new position is better, update the agent agent = self.generate_empty_agent(pos_new) agent.target = tar_new self.pop[idx] = agent else: # If not better, use original GWO update A1 = a * (2 * self.generator.random(self.problem.n_dims) - 1) A2 = a * (2 * self.generator.random(self.problem.n_dims) - 1) A3 = a * (2 * self.generator.random(self.problem.n_dims) - 1) C1 = 2 * self.generator.random(self.problem.n_dims) C2 = 2 * self.generator.random(self.problem.n_dims) C3 = 2 * self.generator.random(self.problem.n_dims) X1 = list_best[0].solution - A1 * np.abs(C1 * list_best[0].solution - self.pop[idx].solution) X2 = list_best[1].solution - A2 * np.abs(C2 * list_best[1].solution - self.pop[idx].solution) X3 = list_best[2].solution - A3 * np.abs(C3 * list_best[2].solution - self.pop[idx].solution) pos_new = (X1 + X2 + X3) / 3.0 pos_new = self.correct_solution(pos_new) tar_new = self.get_target(pos_new) # Create new agent with updated position if self.compare_target(tar_new, self.pop[idx].target, self.problem.minmax): agent = self.generate_empty_agent(pos_new) agent.target = tar_new self.pop[idx] = agent # Apply Opposition-Based Learning (OBL) for leading wolves _, list_best, _ = self.get_special_agents(self.pop, n_best=3, minmax=self.problem.minmax) pop_sorted, indices = self.get_sorted_population(self.pop, minmax=self.problem.minmax, return_index=True) obl_alpha = self.generate_agent(solution=self.problem.lb + self.problem.ub - pop_sorted[0].solution) obl_beta = self.generate_agent(solution=self.problem.lb + self.problem.ub - pop_sorted[1].solution) obl_delta = self.generate_agent(solution=self.problem.lb + self.problem.ub - pop_sorted[2].solution) obl_pop = [obl_alpha, obl_beta, obl_delta] # Replace worst 3 wolves with opposite solutions if they are better for idx in range(0, 3): if self.compare_target(obl_pop[idx].target, self.pop[indices[-3+idx]].target, self.problem.minmax): self.pop[idx] = obl_pop[idx]
[docs]class OGWO(Optimizer): """ The original version of: Opposition-based learning Grey Wolf Optimizer (OGWO) Links: 1. https://doi.org/10.1016/j.knosys.2021.107139 Examples ~~~~~~~~ >>> import numpy as np >>> from mealpy import FloatVar, GWO >>> >>> def objective_function(solution): >>> return np.sum(solution**2) >>> >>> problem_dict = { >>> "bounds": FloatVar(lb=(-10.,) * 30, ub=(10.,) * 30, name="delta"), >>> "minmax": "min", >>> "obj_func": objective_function >>> } >>> >>> model = GWO.OGWO(epoch=1000, pop_size=50, miu_factor=2.0, jumping_rate=0.05) >>> 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] Yu, X., Xu, W., & Li, C. (2021). Opposition-based learning grey wolf optimizer for global optimization. Knowledge-Based Systems, 226, 107139. """ def __init__(self, epoch: int = 10000, pop_size: int = 100, miu_factor: float = 2.0, jumping_rate: float = 0.05, **kwargs: object) -> None: """ Args: epoch (int): maximum number of iterations, default = 10000 pop_size (int): number of population size, default = 100 miu_factor (float): nonlinear coefficient for equation (11), default = 2.0 jumping_rate (float): jumping rate for OBL, default = 0.05 """ 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.miu_factor = self.validator.check_float("miu_factor", miu_factor, [0.0, 10.0]) self.jumping_rate = self.validator.check_float("jumping_rate", jumping_rate, [0.0, 1.0]) self.set_parameters(["epoch", "pop_size", "miu_factor", "jumping_rate"]) self.sort_flag = False
[docs] def initialization(self) -> None: """Initialize population with opposition-based learning""" if self.pop is None: self.pop = self.generate_population(self.pop_size) # Generate opposition population using equation (12) pop_opposite = [] for agent in self.pop: pos_opposite = self.problem.lb + self.problem.ub - agent.solution agent_opposite = self.generate_empty_agent(pos_opposite) agent_opposite.target = self.get_target(pos_opposite) pop_opposite.append(agent_opposite) # Combine original and opposite populations self.pop = self.get_sorted_and_trimmed_population(self.pop + pop_opposite, self.pop_size, minmax=self.problem.minmax)
[docs] def evolve(self, epoch): """ The main operations (equations) of algorithm. Inherit from Optimizer class Args: epoch (int): The current iteration """ # linearly decreased from 2 to 0 a = 2. * (1 - (epoch / self.epoch)**self.miu_factor) _, list_best, _ = self.get_special_agents(self.pop, n_best=3, minmax=self.problem.minmax) pop_new = [] for idx in range(0, self.pop_size): A1 = a * (2 * self.generator.random(self.problem.n_dims) - 1) A2 = a * (2 * self.generator.random(self.problem.n_dims) - 1) A3 = a * (2 * self.generator.random(self.problem.n_dims) - 1) C1 = 2 * self.generator.random(self.problem.n_dims) C2 = 2 * self.generator.random(self.problem.n_dims) C3 = 2 * self.generator.random(self.problem.n_dims) X1 = list_best[0].solution - A1 * np.abs(C1 * list_best[0].solution - self.pop[idx].solution) X2 = list_best[1].solution - A2 * np.abs(C2 * list_best[1].solution - self.pop[idx].solution) X3 = list_best[2].solution - A3 * np.abs(C3 * list_best[2].solution - self.pop[idx].solution) pos_new = (X1 + X2 + X3) / 3.0 pos_new = self.correct_solution(pos_new) agent = self.generate_empty_agent(pos_new) pop_new.append(agent) if self.mode not in self.AVAILABLE_MODES: agent.target = self.get_target(pos_new) 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) # Apply opposition-based learning if self.generator.random() < self.jumping_rate: # Generate opposition population using equation (12) pop_opposite = [] for agent in self.pop: pos_opposite = self.problem.lb + self.problem.ub - agent.solution agent_opposite = self.generate_empty_agent(pos_opposite) agent_opposite.target = self.get_target(pos_opposite) pop_opposite.append(agent_opposite) # Combine original and opposite populations self.pop = self.get_sorted_and_trimmed_population(self.pop + pop_opposite, self.pop_size, minmax=self.problem.minmax)
[docs]class ER_GWO(Optimizer): """ The original version of: Efficient and Robust Grey Wolf Optimizer (ER-GWO) Notes: + Slow convergence speed due to the (miu_factor)^(iteration) ==> Big number + Three more parameters than original GWO, increase the complexity of the algorithm. Links: 1. https://doi.org/10.1007/s00500-019-03939-y Examples ~~~~~~~~ >>> import numpy as np >>> from mealpy import FloatVar, GWO >>> >>> def objective_function(solution): >>> return np.sum(solution**2) >>> >>> problem_dict = { >>> "bounds": FloatVar(lb=(-10.,) * 30, ub=(10.,) * 30, name="delta"), >>> "minmax": "min", >>> "obj_func": objective_function >>> } >>> >>> model = GWO.ER_GWO(epoch=1000, pop_size=50, a_initial=2.0, a_final=0.0, miu_factor=1.0001) >>> 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] Long, W., Cai, S., Jiao, J. et al. An efficient and robust grey wolf optimizer algorithm for large-scale numerical optimization. Soft Comput 24, 997–1026 (2020). """ def __init__(self, epoch: int = 10000, pop_size: int = 100, a_initial: float = 2.0, a_final: float = 0.0, miu_factor: float = 1.0001, **kwargs: object) -> None: """ Args: epoch (int): maximum number of iterations, default = 10000 pop_size (int): number of population size, default = 100 a_initial (float): initial value of coefficient a, default = 2.0 a_final (float): final value of coefficient a, default = 0.0 miu_factor (float): nonlinear coefficient for equation (8), default = 1.0001 """ 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.a_initial = self.validator.check_float("a_initial", a_initial, [0.0, 10.0]) self.a_final = self.validator.check_float("a_final", a_final, [0.0, self.a_initial]) self.miu_factor = self.validator.check_float("miu_factor", miu_factor, [1.0001, 1.01]) # Required in paper self.set_parameters(["epoch", "pop_size", "a_initial", "a_final", "miu_factor"]) self.sort_flag = False
[docs] def evolve(self, epoch): """ The main operations (equations) of algorithm. Inherit from Optimizer class Args: epoch (int): The current iteration """ # linearly decreased from 2 to 0 a = self.a_initial - (self.a_initial - self.a_final) * self.miu_factor ** epoch _, list_best, _ = self.get_special_agents(self.pop, n_best=3, minmax=self.problem.minmax) pop_new = [] for idx in range(0, self.pop_size): A1 = a * (2 * self.generator.random(self.problem.n_dims) - 1) A2 = a * (2 * self.generator.random(self.problem.n_dims) - 1) A3 = a * (2 * self.generator.random(self.problem.n_dims) - 1) C1 = 2 * self.generator.random(self.problem.n_dims) C2 = 2 * self.generator.random(self.problem.n_dims) C3 = 2 * self.generator.random(self.problem.n_dims) X1 = list_best[0].solution - A1 * np.abs(C1 * list_best[0].solution - self.pop[idx].solution) X2 = list_best[1].solution - A2 * np.abs(C2 * list_best[1].solution - self.pop[idx].solution) X3 = list_best[2].solution - A3 * np.abs(C3 * list_best[2].solution - self.pop[idx].solution) dist1 = np.linalg.norm(X1) dist2 = np.linalg.norm(X2) dist3 = np.linalg.norm(X3) total = dist1 + dist2 + dist3 if total == 0: # Avoid division by zero pos_new = (X1 + X2 + X3) / 3.0 else: # Normalize distances to avoid division by zero pos_new = (X1 * dist1 + X2 * dist2 + X3 * dist3) / total pos_new = self.correct_solution(pos_new) agent = self.generate_empty_agent(pos_new) pop_new.append(agent) if self.mode not in self.AVAILABLE_MODES: agent.target = self.get_target(pos_new) 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)
[docs]class CG_GWO(Optimizer): """ The original version of: Cauchy‑Gaussian mutation and improved search strategy GWO (CG‑GWO) Notes: + This algorithm can't be parallelized because of the 'single' update mode. + Meaning that the updating of the pack is based on order and sequence of the wolves. Links: 1. https://doi.org/10.1038/s41598-022-23713-9 Examples ~~~~~~~~ >>> import numpy as np >>> from mealpy import FloatVar, GWO >>> >>> def objective_function(solution): >>> return np.sum(solution**2) >>> >>> problem_dict = { >>> "bounds": FloatVar(lb=(-10.,) * 30, ub=(10.,) * 30, name="delta"), >>> "minmax": "min", >>> "obj_func": objective_function >>> } >>> >>> model = GWO.CG_GWO(epoch=1000, pop_size=50) >>> 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] Li, K., Li, S., Huang, Z. et al. Grey Wolf Optimization algorithm based on Cauchy-Gaussian mutation and improved search strategy. Sci Rep 12, 18961 (2022). """ def __init__(self, epoch: int = 10000, pop_size: int = 100, **kwargs: object) -> None: """ Args: epoch (int): maximum number of iterations, default = 10000 pop_size (int): number of population size, default = 100 """ 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.set_parameters(["epoch", "pop_size"]) self.is_parallelizable = False self.sort_flag = False
[docs] def cauchy_gaussian_mutation(self, best, leader, epoch): """ Apply Cauchy-Gaussian mutation to leader wolves """ # Calculate dynamic parameters (equations 11 and 12) eps2 = (epoch / self.epoch) ** 2 eps1 = 1 - eps2 # Calculate sigma (equation 9) if abs(best.target.fitness) > 1e-10: sigma = np.exp((leader.target.fitness - best.target.fitness) / abs(best.target.fitness)) else: sigma = 1.0 # Generate Cauchy and Gaussian random variables c_rand = self.generator.standard_cauchy(size=self.problem.n_dims) * sigma**2 + 0 g_rand = self.generator.normal(loc=0, scale=sigma**2, size=self.problem.n_dims) # Apply mutation (equation 8) mutated_pos = leader.solution * (1 + eps1 * c_rand + eps2 * g_rand) return mutated_pos
[docs] def evolve(self, epoch): """ The main operations (equations) of algorithm. Inherit from Optimizer class Args: epoch (int): The current iteration """ # linearly decreased from 2 to 0 a = 2 - 2. * epoch / self.epoch _, list_best, _ = self.get_special_agents(self.pop, n_best=3, minmax=self.problem.minmax) # Apply Cauchy-Gaussian mutation to leaders alpha_pos = self.cauchy_gaussian_mutation(list_best[0], list_best[0], epoch) alpha_pos = self.correct_solution(alpha_pos) alpha = self.generate_agent(solution=alpha_pos) beta_pos = self.cauchy_gaussian_mutation(list_best[0], list_best[1], epoch) beta_pos = self.correct_solution(beta_pos) beta = self.generate_agent(solution=beta_pos) delta_pos = self.cauchy_gaussian_mutation(list_best[0], list_best[2], epoch) delta_pos = self.correct_solution(delta_pos) delta = self.generate_agent(solution=delta_pos) leaders = [alpha, beta, delta] # Greedy selection mechanism list_best = self.greedy_selection_population(list_best, leaders, self.problem.minmax) pop_new = [] for idx in range(0, self.pop_size): ## Apply improved search strategy # Apply improved search strategy (equation 13) r1, r2, r3, r4, r5 = self.generator.random(5) if r5 >= 0.5: # Exploration around random wolf # Select random wolf from population jdx = self.generator.choice(list(set(range(self.pop_size)) - {idx})) x_rand = self.pop[jdx].solution pos_new = x_rand - r1 * np.abs(x_rand - 2 * r2 * self.pop[idx].solution) else: # Exploration around alpha wolf # Calculate average position of all wolves x_avg = np.mean([agent.solution for agent in self.pop], axis=0) pos_new = (list_best[0].solution - x_avg) - r3 * (self.problem.lb + r4 * (self.problem.ub - self.problem.lb)) pos_new = self.correct_solution(pos_new) agent = self.generate_agent(pos_new) if self.compare_target(self.pop[idx].target, agent.target, self.problem.minmax): # If new position is not better, use original GWO update A1 = a * (2 * self.generator.random(self.problem.n_dims) - 1) A2 = a * (2 * self.generator.random(self.problem.n_dims) - 1) A3 = a * (2 * self.generator.random(self.problem.n_dims) - 1) C1 = 2 * self.generator.random(self.problem.n_dims) C2 = 2 * self.generator.random(self.problem.n_dims) C3 = 2 * self.generator.random(self.problem.n_dims) X1 = list_best[0].solution - A1 * np.abs(C1 * list_best[0].solution - self.pop[idx].solution) X2 = list_best[1].solution - A2 * np.abs(C2 * list_best[1].solution - self.pop[idx].solution) X3 = list_best[2].solution - A3 * np.abs(C3 * list_best[2].solution - self.pop[idx].solution) pos_new = (X1 + X2 + X3) / 3.0 pos_new = self.correct_solution(pos_new) agent = self.generate_agent(pos_new) if self.compare_target(agent.target, self.pop[idx].target, self.problem.minmax): # If new position is better, update the agent self.pop[idx] = agent