Source code for mealpy.swarm_based.FDO

#!/usr/bin/env python
# Created by "Thieu" at 10:01, 16/08/2025 ----------%                                                                               
#       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


[docs]class OriginalFDO(Optimizer): """ The original version of: Fitness Dependent Optimizer (FDO) Warnings -------- 1. Inspired by the bee swarming reproductive process, this algorithm optimizes solutions based on their fitness values by relying primarily on Lévy flight techniques. Owing to random number generation following the Lévy distribution, the algorithm demonstrates strong convergence capabilities. 2. However, a major drawback lies in its fitness weight design, where an update is virtually impossible when the fitness weight equals 1 References ---------- [1] Abdullah, J. M., & Ahmed, T. (2019). Fitness dependent optimizer: inspired by the bee swarming reproductive process. IEEe Access, 7, 43473-43486. https://doi.org/10.1109/ACCESS.2019.2907012 Examples ~~~~~~~~ >>> import numpy as np >>> from mealpy import FloatVar, FDO >>> >>> 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 = FDO.OriginalFDO(epoch=1000, pop_size=50, weight_factor=0.1) >>> 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="Fitness Dependent Optimizer", year=2019, difficulty="medium", kind="original") def __init__(self, epoch: int = 10000, pop_size: int = 100, weight_factor=0.1, **kwargs: object) -> None: """ Args: epoch (int): maximum number of iterations, default = 10000 pop_size (int): number of population size, default = 100 weight_factor (float): factor to adjust the fitness weight calculation, default = 0.1 """ 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.weight_factor = self.validator.check_float("weight_factor", weight_factor, [0.0, 1.0]) self.set_parameters(["epoch", "pop_size", "weight_factor"]) self.sort_flag = False self.is_parallelizable = False
[docs] def before_main_loop(self): self.pop_pace = [0, ] * self.pop_size
[docs] def get_fit_weight(self, best_fit, current_fit, weight_factor=0.1): """ Calculate the fitness weight based on the best and current fitness values. Args: best_fit (float): The best fitness value found so far. current_fit (float): The current fitness value of the agent. weight_factor (float): A factor to adjust the weight calculation, default is 0.1. Returns: float: The fitness weight. """ if best_fit == 0: return 0 else: if self.problem.minmax == "min": if best_fit < (0.05 * current_fit): return 0.2 else: return best_fit / current_fit - weight_factor else: if best_fit > (0.05 * current_fit): return 0.2 else: return weight_factor - best_fit / current_fit
[docs] def get_into_levy_bound(self, pos_new): """ Ensure the new position is within the levy bounds. Args: pos_new (np.ndarray): The new position to be checked. Returns: np.ndarray: The position clipped to the problem bounds. """ levy = self.get_levy_flight_step(beta=1.5, multiplier=0.01, size=self.problem.n_dims, case=-1) levy_up = self.problem.ub * np.abs(levy) levy_lb = self.problem.lb * np.abs(levy) pos_new = np.select( [pos_new > self.problem.ub, pos_new < self.problem.lb], [levy_up, levy_lb], default=pos_new ) return pos_new
[docs] def evolve(self, epoch): """ The main operations (equations) of algorithm. Inherit from Optimizer class Args: epoch (int): The current iteration """ # Update positions for each thief for idx in range(self.pop_size): fw = self.get_fit_weight(self.g_best.target.fitness, self.pop[idx].target.fitness, self.weight_factor) dist = self.g_best.solution - self.pop[idx].solution levy = self.get_levy_flight_step(beta=1.5, multiplier=0.01, size=self.problem.n_dims, case=-1) if fw == 1: pace = self.pop[idx].solution * levy elif fw == 0: pace = dist * levy else: pace = dist * fw * np.sign(levy) self.pop_pace[idx] = pace pos_new = self.pop[idx].solution + pace pos_new = self.get_into_levy_bound(pos_new) pos_new = self.correct_solution(pos_new) agent = self.generate_agent(pos_new) # Check if new position is better if self.compare_target(agent.target, self.pop[idx].target, self.problem.minmax): self.pop[idx] = agent else: # Alternative update strategy dist = self.g_best.solution - pos_new pos_new = pos_new + (dist * fw) + self.pop_pace[idx] pos_new = self.get_into_levy_bound(pos_new) 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): self.pop[idx] = agent else: # Third update strategy levy = self.get_levy_flight_step(beta=1.5, multiplier=0.01, size=self.problem.n_dims, case=-1) pos_new = self.pop[idx].solution + self.pop[idx].solution * levy pos_new = self.get_into_levy_bound(pos_new) 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): self.pop[idx] = agent