Source code for mealpy.swarm_based.WSO

#!/usr/bin/env python
# Created by "Thieu" at 22:35, 16/07/2026 ----------%
#       Email: nguyenthieu2102@gmail.com            %                                                    
#       Github: https://github.com/thieu1995        %                         
# --------------------------------------------------%

import numpy as np
from mealpy.optimizer import Optimizer
from mealpy.utils.opt_info import OptInfo, ScientificConcern


[docs]class OriginalWSO(Optimizer): """ The original version: White Shark Optimizer (WSO) Parameters ---------- epoch : int Maximum number of iterations, in range [1, 100000]. Default is 10000. pop_size : int Population size, in range [5, 100000]. Default is 100. tau : float Acceleration coefficient used to derive the constriction factor `mu`, in range [0.0, 100.0]. Default is 4.125. p_min : float Initial velocities to control the effect of global and local best positions, in range [0.0, 10.0]. Default is 0.5. p_max : float Subordinate velocities to control the effect of global and local best positions, in range [0.0, 100.0]. Default is 1.5. f_min : float Minimum frequencies of the undulating motion, in range (0.0, 10.0). Default is 0.07. f_max : float Maximum frequencies of the undulating motion, in range (0.0, 10.0). Default is 0.75. a0 : float Constant managing exploration vs. exploitation via the movement force parameter `mv` (hearing/smell strength), in range (0.0, 1000.0). Default is 6.25. a1 : float Constant managing exploration vs. exploitation via the movement force parameter `mv` (hearing/smell strength), in range (0.0, 1000.0). Default is 100.0. a2 : float Constant controlling the sight/smell strength when following the best shark in the school (`s_s`), in range (0.0, 1000.0). Default is 0.0005. Warnings -------- 1. Discrepancies have been spotted between the MATLAB code and the pseudocode presented in the algorithm's paper. Users should exercise caution when using this algorithm. 2. This version accurately implements the equations from the paper, allowing users to validate both the algorithm's performance and the published results. 3. A drawback of this algorithm is the introduction of too many meaningless parameters. Replacing them with simpler operators could potentially improve performance while eliminating the need for parameter tuning 4. Many parameters are fixed in the paper, but this heavily depends on your specific problem. Therefore, users are advised to read the paper carefully to understand the functional meaning of these hyperparameters. Links ----- 1. https://doi.org/10.1016/j.knosys.2022.108457 2. https://www.mathworks.com/matlabcentral/fileexchange/107365-white-shark-optimizer-wso References ---------- 1. Braik, M., Hammouri, A., Atwan, J., Al-Betar, M. A., & Awadallah, M. A. (2022). White Shark Optimizer: A novel bio-inspired meta-heuristic algorithm for global optimization problems. Knowledge-Based Systems, 243, 108457. Examples -------- >>> import numpy as np >>> from mealpy import FloatVar, WSO >>> >>> 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 = WSO.OriginalWSO(epoch=1000, pop_size=50, tau=4.2, p_min=0.5, p_max=2.0, f_min=0.1, f_max=0.8, a0=6, a1=100, a2=0.001) >>> 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}") """ OPT_INFO = OptInfo(name=" White Shark Optimizer", year=2022, difficulty="nightmare", kind="original", scientific_status="questionable", concerns=( ScientificConcern.CODE_PSEUDOCODE_MISMATCH, ScientificConcern.LACK_OF_NOVELTY, ScientificConcern.POOR_REPRODUCIBILITY, ScientificConcern.FABRICATED_RESULTS, )) def __init__(self, epoch=10000, pop_size=100, tau: float=4.125, p_min: float=0.5, p_max: float=1.5, f_min: float=0.07, f_max: float=0.75, a0: float=6.25, a1: float=100.0, a2: float=0.0005, **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, 100000]) self.tau = self.validator.check_float("tau", tau, [0, 100.]) self.p_min = self.validator.check_float("p_min", p_min, [0, 10.]) self.p_max = self.validator.check_float("p_max", p_max, [0, 100.]) self.f_min = self.validator.check_float("f_min", f_min, (0, 10.)) self.f_max = self.validator.check_float("f_max", f_max, (0, 10.)) self.a0 = self.validator.check_float("a0", a0, (0, 1000.)) self.a1 = self.validator.check_float("a1", a1, (0, 1000.)) self.a2 = self.validator.check_float("a2", a2, (0, 1000.)) self.set_parameters(["epoch", "pop_size", "tau", "p_min", "p_max", "f_min", "f_max", "a0", "a1", "a2"]) self.sort_flag = False self.v = None
[docs] def before_main_loop(self): self.v = np.zeros((self.pop_size, self.problem.n_dims)) # Pre-calculate the constriction factor mu (Eq. 9) self.mu = 2.0 / abs(2.0 - self.tau - np.sqrt(self.tau ** 2 - 4.0 * self.tau))
[docs] def evolve(self, epoch): """ The main evolution step. """ # Dynamically update optimization control parameters as functions of current iteration p1 = self.p_max + (self.p_max - self.p_min) * np.exp(-((4.0 * epoch / self.epoch) ** 2)) p2 = self.p_min + (self.p_max - self.p_min) * np.exp(-((4.0 * epoch / self.epoch) ** 2)) # mv: Movement force representing hearing and smell strength (Eq. 15) mv = 1.0 / (self.a0 + np.exp((self.epoch / 2.0 - epoch) / self.a1)) # s_s: Senses of smell and sight for fish school tracking behavior (Eq. 18) s_s = abs(1.0 - np.exp(-self.a2 * epoch / self.epoch)) # Identify the best known position vector known to the swarm (v_index) (Eq. 6) pos_list = np.array([agent.solution for agent in self.pop]) v_idx = np.floor(self.pop_size * self.generator.uniform(0, 1, self.pop_size)).astype(int) w_best_v = pos_list[v_idx] c1 = self.generator.uniform(0, 1, (self.pop_size, self.problem.n_dims)) c2 = self.generator.uniform(0, 1, (self.pop_size, self.problem.n_dims)) # Velocity update formulation (Eq. 5) v = self.mu * (self.v + p1 * (self.g_best.solution - pos_list) * c1 + p2 * (w_best_v - pos_list) * c2) # Generate wave frequencies for undulating motion (Eq. 14) f = self.f_min + (self.f_max - self.f_min) * self.generator.uniform(0, 1, self.pop_size) w_new = np.zeros_like(pos_list) rand_vals = self.generator.uniform(0, 1, self.pop_size) for idx in range(self.pop_size): # Position update Step 1: Movement towards prey (Eq. 10) if rand_vals[idx] < mv: # Random exploration bounding vectors a = (pos_list[idx] - self.problem.ub > 0).astype(int) b = (pos_list[idx] - self.problem.lb < 0).astype(int) w_o = np.bitwise_xor(a, b) # Apply logical mapping for random target tracking around the optimal prey w_new[idx] = pos_list[idx] * np.logical_not(w_o) + self.problem.ub * a + self.problem.lb * b else: # Move towards prey using undulating wavy motion w_new[idx] = pos_list[idx] + v[idx] / f[idx] # Position update Step 2: Fish school behavior and movement towards the best shark for idx in range(self.pop_size): # Ensure intermediate position stays within boundaries before applying collective behavior w_new[idx] = np.clip(w_new[idx], self.problem.lb, self.problem.ub) # Calculate distance between prey and white shark (Eq. 17) d_w = abs(self.generator.uniform(0, 1, self.problem.n_dims) * (self.g_best.solution - w_new[idx])) r1 = self.generator.uniform(0, 1, self.problem.n_dims) r2 = self.generator.uniform(0, 1, self.problem.n_dims) # Emulate collective behavior near optimal target (Eq. 16, 19) if idx < s_s * self.pop_size: sgn = np.where(r2 - 0.5 > 0, 1, -1) w_hat = self.g_best.solution + r1 * d_w * sgn # Position update respecting the fish school consensus (Eq. 19) w_new[idx] = (w_new[idx] + w_hat) / (2.0 * self.generator.uniform(0, 1, self.problem.n_dims)) # Boundary and update agent pop_new = [] for idx in range(self.pop_size): pos_new = self.correct_solution(w_new[idx]) # agent = self.generate_empty_agent(pos_new) pop_new.append(self.generate_empty_agent(pos_new)) # Update fitness in single mode if self.mode not in self.AVAILABLE_MODES: pop_new[-1].target = self.get_target(pos_new) self.pop[idx] = self.get_better_agent(pop_new[-1], self.pop[idx], self.problem.minmax) # Update fitness in parallel modes 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)