#!/usr/bin/env python
# Created by "Thieu" at 14:52, 17/03/2020 ----------%
# Email: nguyenthieu2102@gmail.com %
# Github: https://github.com/thieu1995 %
# --------------------------------------------------%
import numpy as np
from mealpy.optimizer import Optimizer
[docs]class OriginalMRFO(Optimizer):
"""
The original version of: Manta Ray Foraging Optimization (MRFO)
Links:
1. https://doi.org/10.1016/j.engappai.2019.103300
Hyper-parameters should fine-tune in approximate range to get faster convergence toward the global optimum:
+ somersault_range (float): [1.5, 3], somersault factor that decides the somersault range of manta rays, default=2
Examples
~~~~~~~~
>>> import numpy as np
>>> from mealpy import FloatVar, MRFO
>>>
>>> def objective_function(solution):
>>> return np.sum(solution**2)
>>>
>>> problem_dict = {
>>> "bounds": FloatVar(n_vars=30, lb=(-10.,) * 30, ub=(10.,) * 30, name="delta"),
>>> "minmax": "min",
>>> "obj_func": objective_function
>>> }
>>>
>>> model = MRFO.OriginalMRFO(epoch=1000, pop_size=50, somersault_range = 2.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] Zhao, W., Zhang, Z. and Wang, L., 2020. Manta ray foraging optimization: An effective bio-inspired
optimizer for engineering applications. Engineering Applications of Artificial Intelligence, 87, p.103300.
"""
def __init__(self, epoch: int = 10000, pop_size: int = 100, somersault_range: float = 2.0, **kwargs: object) -> None:
"""
Args:
epoch (int): maximum number of iterations, default = 10000
pop_size (int): number of population size, default = 100
somersault_range (float): somersault factor that decides the somersault range of manta rays, default=2
"""
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.somersault_range = self.validator.check_float("somersault_range", somersault_range, [1.0, 5.0])
self.set_parameters(["epoch", "pop_size", "somersault_range"])
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
"""
pop_new = []
for idx in range(0, self.pop_size):
# Cyclone foraging (Eq. 5, 6, 7)
if self.generator.random() < 0.5:
r1 = self.generator.uniform()
beta = 2 * np.exp(r1 * (self.epoch - epoch) / self.epoch) * np.sin(2 * np.pi * r1)
if (epoch + 1) / self.epoch < self.generator.random():
x_rand = self.generator.uniform(self.problem.lb, self.problem.ub)
if idx == 0:
x_t1 = x_rand + self.generator.uniform() * (x_rand - self.pop[idx].solution) + \
beta * (x_rand - self.pop[idx].solution)
else:
x_t1 = x_rand + self.generator.uniform() * (self.pop[idx - 1].solution - self.pop[idx].solution) + \
beta * (x_rand - self.pop[idx].solution)
else:
if idx == 0:
x_t1 = self.g_best.solution + self.generator.uniform() * (self.g_best.solution - self.pop[idx].solution) + \
beta * (self.g_best.solution - self.pop[idx].solution)
else:
x_t1 = self.g_best.solution + self.generator.uniform() * (self.pop[idx - 1].solution - self.pop[idx].solution) + \
beta * (self.g_best.solution - self.pop[idx].solution)
# Chain foraging (Eq. 1,2)
else:
r = self.generator.uniform()
alpha = 2 * r * np.sqrt(np.abs(np.log(r)))
if idx == 0:
x_t1 = self.pop[idx].solution + r * (self.g_best.solution - self.pop[idx].solution) + \
alpha * (self.g_best.solution - self.pop[idx].solution)
else:
x_t1 = self.pop[idx].solution + r * (self.pop[idx - 1].solution - self.pop[idx].solution) + \
alpha * (self.g_best.solution - self.pop[idx].solution)
pos_new = self.correct_solution(x_t1)
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(self.pop[idx], agent, 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)
_, g_best = self.update_global_best_agent(self.pop, save=False)
pop_child = []
for idx in range(0, self.pop_size):
# Somersault foraging (Eq. 8)
x_t1 = self.pop[idx].solution + self.somersault_range * \
(self.generator.uniform() * g_best.solution - self.generator.uniform() * self.pop[idx].solution)
pos_new = self.correct_solution(x_t1)
agent = self.generate_empty_agent(pos_new)
pop_child.append(agent)
if self.mode not in self.AVAILABLE_MODES:
agent.target = self.get_target(pos_new)
self.pop[idx] = self.get_better_agent(self.pop[idx], agent, self.problem.minmax)
if self.mode in self.AVAILABLE_MODES:
pop_child = self.update_target_for_population(pop_child)
self.pop = self.greedy_selection_population(self.pop, pop_child, self.problem.minmax)
[docs]class WMQIMRFO(Optimizer):
"""
The original version of: Wavelet Mutation and Quadratic Interpolation MRFO (WMQIMRFO)
Links:
1. https://doi.org/10.1016/j.knosys.2021.108071
Hyper-parameters should fine-tune in approximate range to get faster convergence toward the global optimum:
+ somersault_range (float): [1.5, 3], somersault factor that decides the somersault range of manta rays, default=2
+ pm (float): (0.0, 1.0), probability mutation, default = 0.5
Examples
~~~~~~~~
>>> import numpy as np
>>> from mealpy import FloatVar, MRFO
>>>
>>> def objective_function(solution):
>>> return np.sum(solution**2)
>>>
>>> problem_dict = {
>>> "bounds": FloatVar(n_vars=30, lb=(-10.,) * 30, ub=(10.,) * 30, name="delta"),
>>> "minmax": "min",
>>> "obj_func": objective_function
>>> }
>>>
>>> model = MRFO.OriginalMRFO(epoch=1000, pop_size=50, somersault_range = 2.0, pm=0.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] G. Hu, M. Li, X. Wang et al., An enhanced manta ray foraging optimization algorithm for shape optimization of
complex CCG-Ball curves, Knowledge-Based Systems (2022), doi: https://doi.org/10.1016/j.knosys.2021.108071.
"""
def __init__(self, epoch: int = 10000, pop_size: int = 100, somersault_range: float = 2.0, pm: float = 0.5, **kwargs: object) -> None:
"""
Args:
epoch (int): maximum number of iterations, default = 10000
pop_size (int): number of population size, default = 100
somersault_range (float): somersault factor that decides the somersault range of manta rays, default=2
pm (float): probability mutation, default = 0.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.somersault_range = self.validator.check_float("somersault_range", somersault_range, [1.0, 5.0])
self.pm = self.validator.check_float("pm", pm, (0.0, 1.0))
self.set_parameters(["epoch", "pop_size", "somersault_range", "pm"])
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
"""
pop_new = []
for idx in range(0, self.pop_size):
x_t = self.pop[idx].solution
x_t1 = self.pop[idx-1].solution
## Morlet wavelet mutation strategy
## Goal is to jump out of local optimum --> Performed in exploration stage
s_constant = 2.0
a = s_constant * (1.0 / s_constant) ** (1.0 - epoch / self.epoch)
theta = self.generator.uniform(-2.5 * a, 2.5 * a)
x = theta / a
w = np.exp(-x ** 2 / 2) * np.cos(5 * x)
xichma = 1.0 / np.sqrt(a) * w
if self.generator.random() < 0.5: # Control parameter adjustment
coef = np.log(1 + (np.e - 1.) * epoch / self.epoch) # Eq. 3.11
r1 = self.generator.uniform()
beta = 2 * np.exp(r1 * (self.epoch - epoch) / self.epoch) * np.sin(2 * np.pi * r1)
if coef < self.generator.random(): # Cyclone foraging
x_rand = self.problem.generate_solution()
if self.generator.random() < self.pm: # Morlet wavelet mutation
if idx == 0:
pos_new = x_rand + self.generator.random() * (x_rand - x_t) + beta * (x_rand - x_t)
else:
pos_new = x_rand + self.generator.random() * (x_t1 - x_t) + beta * (x_rand - x_t)
else:
conditions = self.generator.uniform(0, 1, self.problem.n_dims) > 0.5
if idx == 0:
t1 = x_rand + self.generator.random(self.problem.n_dims) * (x_rand - x_t) + beta * (x_rand - x_t) + xichma * (self.problem.ub - x_t)
t2 = x_rand + self.generator.random(self.problem.n_dims) * (x_rand - x_t) + beta * (x_rand - x_t) + xichma * (x_t - self.problem.lb)
else:
t1 = x_rand + self.generator.random(self.problem.n_dims) * (x_t1 - x_t) + beta * (x_rand - x_t) + xichma * (self.problem.ub - x_t)
t2 = x_rand + self.generator.random(self.problem.n_dims) * (x_t1 - x_t) + beta * (x_rand - x_t) + xichma * (x_t - self.problem.lb)
pos_new = np.where(conditions, t1, t2)
else:
if idx == 0:
pos_new = self.g_best.solution + self.generator.random() * (self.g_best.solution - x_t) + beta * (self.g_best.solution - x_t)
else:
pos_new = self.g_best.solution + self.generator.random() * (x_t1 - x_t) + beta * (self.g_best.solution - x_t)
else: # Chain foraging (Eq. 1,2)
r = self.generator.random()
alpha = 2 * r * np.sqrt(np.abs(np.log(r)))
if idx == 0:
pos_new = x_t + r * (self.g_best.solution - x_t) + alpha * (self.g_best.solution - x_t)
else:
pos_new = x_t + r * (x_t1 - x_t) + alpha * (self.g_best.solution - x_t)
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(self.pop[idx], agent, 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)
_, g_best = self.update_global_best_agent(self.pop, save=False)
# Somersault foraging (Eq. 8)
pop_child = []
for idx in range(0, self.pop_size):
pos_new = self.pop[idx].solution + self.somersault_range * \
(self.generator.random() * g_best.solution - self.generator.random() * self.pop[idx].solution)
pos_new = self.correct_solution(pos_new)
agent = self.generate_empty_agent(pos_new)
pop_child.append(agent)
if self.mode not in self.AVAILABLE_MODES:
agent.target = self.get_target(pos_new)
self.pop[idx] = self.get_better_agent(self.pop[idx], agent, self.problem.minmax)
if self.mode in self.AVAILABLE_MODES:
pop_child = self.update_target_for_population(pop_child)
self.pop = self.greedy_selection_population(self.pop, pop_child, self.problem.minmax)
self.pop, g_best = self.update_global_best_agent(self.pop, save=False)
# Quadratic Interpolation
pop_new = []
for idx in range(0, self.pop_size):
idx2, idx3 = idx + 1, idx + 2
if idx == self.pop_size-2:
idx2, idx3 = idx + 1, 0
if idx == self.pop_size-1:
idx2, idx3 = 0, 1
f1, f2, f3 = self.pop[idx].target.fitness, self.pop[idx2].target.fitness, self.pop[idx3].target.fitness
x1, x2, x3 = self.pop[idx].solution, self.pop[idx2].solution, self.pop[idx3].solution
a = f1 / ((x1 - x2) * (x1 - x3)) + f2 / ((x2 - x1) * (x2 - x3)) + f3 / ((x3 - x1) * (x3 - x2))
gx = ((x3 ** 2 - x2 ** 2) * f1 + (x1 ** 2 - x3 ** 2) * f2 + (x2 ** 2 - x1 ** 2) * f3) / (2 * ((x3 - x2) * f1 + (x1 - x3) * f2 + (x2 - x1) * f3))
pos_new = np.where(a > 0, gx, x1)
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(self.pop[idx], agent, 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)