From 52d0495824d8ddc8f4f3d090edaad2d7164f7ce0 Mon Sep 17 00:00:00 2001 From: Furkan Buyukyozgat Date: Mon, 5 Jan 2026 15:38:32 +0300 Subject: [PATCH 1/2] Add Owl Search Algorithm (OSA) optimizer --- mealpy/__init__.py | 2 +- mealpy/swarm_based/OSA.py | 88 +++++++++++++++++++++++++++++++++++ tests/swarm_based/test_OSA.py | 33 +++++++++++++ 3 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 mealpy/swarm_based/OSA.py create mode 100644 tests/swarm_based/test_OSA.py diff --git a/mealpy/__init__.py b/mealpy/__init__.py index b5ba005f..dac4bce8 100644 --- a/mealpy/__init__.py +++ b/mealpy/__init__.py @@ -41,7 +41,7 @@ DMOA, DO, EHO, ESOA, FA, FFA, FFO, FOA, FOX, GJO, GOA, GTO, GWO, HBA, HGS, HHO, JA, MFO, MGO, MPA, MRFO, MSA, NGO, NMRA, OOA, PFA, POA, PSO, SCSO, SeaHO, ServalOA, SFO, SHO, SLO, SRSR, SSA, SSO, SSpiderA, SSpiderO, STO, TDO, TSO, WaOA, WOA, ZOA, - EPC, SMO, SquirrelSA, FDO) + EPC, SMO, SquirrelSA, FDO, OSA) from .system_based import AEO, GCO, WCA from .music_based import HS from .sota_based import LSHADEcnEpSin, IMODE diff --git a/mealpy/swarm_based/OSA.py b/mealpy/swarm_based/OSA.py new file mode 100644 index 00000000..0e9f187e --- /dev/null +++ b/mealpy/swarm_based/OSA.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python +# Created by "Furkan Buyukyozgat" at 15:25, 05/01/2026-------% +# Email: furkanbuyuky@gmail.com % +# Github: https://github.com/furkanbuyuky % +# -----------------------------------------------------------% + +import numpy as np +from mealpy.optimizer import Optimizer + + +class OriginalOSA(Optimizer): + """ + Original Owl Search Algorithm (OSA). + + Main steps: + - Normalize intensity using best/worst fitness + - Compute distance to best + - Intensity correction: ic = I / (r^2 + eps) + rand() + - Update position: x_new = x +/- beta * ic * |alpha * best - x| + - Apply bounds and greedy selection + """ + + def __init__( + self, + epoch=10000, + pop_size=100, + beta_max=1.9, + alpha_max=0.5, + eps=1e-12, + **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.beta_max = self.validator.check_float("beta_max", beta_max, (0.0, 10.0)) + self.alpha_max = self.validator.check_float("alpha_max", alpha_max, (0.0, 1.0)) + self.eps = float(eps) + + self.set_parameters(["epoch", "pop_size", "beta_max", "alpha_max"]) + self.sort_flag = False + + self.alpha = None + + def _beta(self, epoch: int) -> float: + iter_ = epoch + 1 + return self.beta_max - self.beta_max * (iter_ / float(self.epoch)) + + def _get_worst_agent_current_pop(self): + fits = np.array([ag.target.fitness for ag in self.pop], dtype=float) + if self.problem.minmax == "min": + idx = int(np.argmax(fits)) + else: + idx = int(np.argmin(fits)) + return self.pop[idx] + + def evolve(self, epoch: int) -> None: + if self.alpha is None: + self.alpha = self.generator.random() * self.alpha_max + + pvm = self.generator.random() + beta = self._beta(epoch) + + bestowl = self.g_best + weakowl = self._get_worst_agent_current_pop() + + denom = (weakowl.target.fitness - bestowl.target.fitness) + if np.abs(denom) < self.eps: + denom = self.eps + + for i in range(self.pop_size): + x = self.pop[i].solution + + intensity = (self.pop[i].target.fitness - bestowl.target.fitness) / denom + r = np.linalg.norm(x - bestowl.solution) + ic = (intensity / (r * r + self.eps)) + self.generator.random() + + step = beta * ic * np.abs(self.alpha * bestowl.solution - x) + if pvm < 0.5: + x_new = x + step + else: + x_new = x - step + + x_new = self.correct_solution(x_new) + agent_new = self.generate_agent(x_new) + + if self.compare_target(agent_new.target, self.pop[i].target, self.problem.minmax): + self.pop[i] = agent_new diff --git a/tests/swarm_based/test_OSA.py b/tests/swarm_based/test_OSA.py new file mode 100644 index 00000000..88e2bc17 --- /dev/null +++ b/tests/swarm_based/test_OSA.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python +# Created by "Furkan Buyukyozgat" at 15:25, 05/01/2026-------% +# Email: furkanbuyuky@gmail.com % +# Github: https://github.com/furkanbuyuky % +# -----------------------------------------------------------% + +import numpy as np +import pytest + +from mealpy import FloatVar, OSA, Optimizer + + +@pytest.fixture(scope="module") +def problem(): + def objective_function(solution): + return np.sum(solution ** 2) + + problem = { + "obj_func": objective_function, + "bounds": FloatVar(lb=[-10, -15, -4, -2, -8], ub=[10, 15, 12, 8, 20]), + "minmax": "min", + "log_to": None + } + return problem + + +def test_OSA_results(problem): + model = OSA.OriginalOSA(epoch=10, pop_size=50, beta_max=1.9, alpha_max=0.5) + g_best = model.solve(problem) + + assert isinstance(model, Optimizer) + assert isinstance(g_best.solution, np.ndarray) + assert len(g_best.solution) == len(model.problem.lb) From 31d7027634aff6d0ee5e4b34cb006785645ef1fe Mon Sep 17 00:00:00 2001 From: Furkan Buyukyozgat Date: Mon, 5 Jan 2026 15:49:24 +0300 Subject: [PATCH 2/2] Update README to include OSA optimizer --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 47f94383..dca1b99c 100644 --- a/README.md +++ b/README.md @@ -3184,6 +3184,9 @@ All visualization examples: [Link](https://mealpy.readthedocs.io/en/latest/pages ### O +* **OSA - Owl Search Algorithm** + * **OriginalOSA**: Jain, M., Maurya, S., Rani, A., & Singh, V. (2018). Owl search algorithm: A novel nature-inspired heuristic paradigm for global optimization. Journal of Intelligent & Fuzzy Systems, 34, 1573–1582. + ### P * **PSO - Particle Swarm Optimization**