# !/usr/bin/env python
# Created by "Thieu" at 15:37, 19/03/2021 ----------%
# Email: nguyenthieu2102@gmail.com %
# Github: https://github.com/thieu1995 %
# --------------------------------------------------%
import numpy as np
from copy import deepcopy
from mealpy.optimizer import Optimizer
[docs]class OriginalHGS(Optimizer):
"""
The original version of: Hunger Games Search (HGS)
Links:
https://aliasgharheidari.com/HGS.html
Hyper-parameters should fine tuned in approximate range to get faster convergen toward the global optimum:
+ PUP (float): [0.01, 0.2], The probability of updating position (L in the paper), default = 0.08
+ LH (float): [1000, 20000], Largest hunger / threshold, default = 10000
Examples
~~~~~~~~
>>> import numpy as np
>>> from mealpy.swarm_based.HGS import OriginalHGS
>>>
>>> def fitness_function(solution):
>>> return np.sum(solution**2)
>>>
>>> problem_dict1 = {
>>> "fit_func": fitness_function,
>>> "lb": [-10, -15, -4, -2, -8],
>>> "ub": [10, 15, 12, 8, 20],
>>> "minmax": "min",
>>> "verbose": True,
>>> }
>>>
>>> epoch = 1000
>>> pop_size = 50
>>> PUP = 0.08
>>> LH = 10000
>>> model = OriginalHGS(problem_dict1, epoch, pop_size, PUP, LH)
>>> best_position, best_fitness = model.solve()
>>> print(f"Solution: {best_position}, Fitness: {best_fitness}")
References
~~~~~~~~~~
[1] Yang, Y., Chen, H., Heidari, A.A. and Gandomi, A.H., 2021. Hunger games search: Visions, conception, implementation,
deep analysis, perspectives, and towards performance shifts. Expert Systems with Applications, 177, p.114864.
"""
ID_HUN = 2 # ID for Hunger value
def __init__(self, problem, epoch=10000, pop_size=100, PUP=0.08, LH=10000, **kwargs):
"""
Args:
problem (dict): The problem dictionary
epoch (int): maximum number of iterations, default = 10000
pop_size (int): number of population size, default = 100
PUP (float): The probability of updating position (L in the paper), default = 0.08
LH (float): Largest hunger / threshold, default = 10000
"""
super().__init__(problem, kwargs)
self.nfe_per_epoch = pop_size
self.sort_flag = False
self.epoch = epoch
self.pop_size = pop_size
self.PUP = PUP
self.LH = LH
[docs] def create_solution(self):
"""
To get the position, fitness wrapper, target and obj list
+ A[self.ID_POS] --> Return: position
+ A[self.ID_TAR] --> Return: [target, [obj1, obj2, ...]]
+ A[self.ID_TAR][self.ID_FIT] --> Return: target
+ A[self.ID_TAR][self.ID_OBJ] --> Return: [obj1, obj2, ...]
Returns:
list: wrapper of solution with format [position, [target, [obj1, obj2, ...]], hunger]
"""
position = np.random.uniform(self.problem.lb, self.problem.ub)
position = self.amend_position(position)
fitness = self.get_fitness_position(position=position)
hunger = 1.0
return [position, fitness, hunger]
[docs] def sech(self, x):
if np.abs(x) > 50:
return 0.5
return 2 / (np.exp(x) + np.exp(-x))
[docs] def update_hunger_value(self, pop=None, g_best=None, g_worst=None):
# min_index = pop.index(min(pop, key=lambda x: x[self.ID_TAR][self.ID_FIT]))
# Eq (2.8) and (2.9)
for i in range(0, self.pop_size):
r = np.random.rand()
# space: since we pass lower bound and upper bound as list. Better take the np.mean of them.
space = np.mean(self.problem.ub - self.problem.lb)
H = (pop[i][self.ID_TAR][self.ID_FIT] - g_best[self.ID_TAR][self.ID_FIT]) / \
(g_worst[self.ID_TAR][self.ID_FIT] - g_best[self.ID_TAR][self.ID_FIT] + self.EPSILON) * r * 2 * space
if H < self.LH:
H = self.LH * (1 + r)
pop[i][self.ID_HUN] += H
if g_best[self.ID_TAR][self.ID_FIT] == pop[i][self.ID_TAR][self.ID_FIT]:
pop[i][self.ID_HUN] = 0
return pop
[docs] def evolve(self, epoch):
"""
The main operations (equations) of algorithm. Inherit from Optimizer class
Args:
epoch (int): The current iteration
"""
## Eq. (2.2)
### Find the current best and current worst
g_best, g_worst = self.get_global_best_global_worst_solution(self.pop)
pop = self.update_hunger_value(self.pop, g_best, g_worst)
## Eq. (2.4)
shrink = 2 * (1 - (epoch + 1) / self.epoch)
total_hunger = np.sum([pop[idx][self.ID_HUN] for idx in range(0, self.pop_size)])
pop_new = []
for idx in range(0, self.pop_size):
current_agent = deepcopy(self.pop[idx])
#### Variation control
E = self.sech(current_agent[self.ID_TAR][self.ID_FIT] - g_best[self.ID_TAR][self.ID_FIT])
# R is a ranging controller added to limit the range of activity, in which the range of R is gradually reduced to 0
R = 2 * shrink * np.random.rand() - shrink # Eq. (2.3)
## Calculate the hungry weight of each position
if np.random.rand() < self.PUP:
W1 = current_agent[self.ID_HUN] * self.pop_size / (total_hunger + self.EPSILON) * np.random.rand()
else:
W1 = 1
W2 = (1 - np.exp(-abs(current_agent[self.ID_HUN] - total_hunger))) * np.random.rand() * 2
### Udpate position of individual Eq. (2.1)
r1 = np.random.rand()
r2 = np.random.rand()
if r1 < self.PUP:
pos_new = current_agent[self.ID_POS] * (1 + np.random.normal(0, 1))
else:
if r2 > E:
pos_new = W1 * g_best[self.ID_POS] + R * W2 * abs(g_best[self.ID_POS] - current_agent[self.ID_POS])
else:
pos_new = W1 * g_best[self.ID_POS] - R * W2 * abs(g_best[self.ID_POS] - current_agent[self.ID_POS])
current_agent[self.ID_POS] = self.amend_position(pos_new)
pop_new.append(current_agent)
self.pop = self.update_fitness_population(pop_new)