#!/usr/bin/env python
# Created by "Thieu" at 16:58, 08/04/2020 ----------%
# Email: nguyenthieu2102@gmail.com %
# Github: https://github.com/thieu1995 %
# --------------------------------------------------%
import numpy as np
from copy import deepcopy
from mealpy.optimizer import Optimizer
[docs]class BaseGSKA(Optimizer):
"""
My changed version of: Gaining Sharing Knowledge-based Algorithm (GSKA)
Notes
~~~~~
+ I remove all the third loop, remove 2 parameters
+ Solution represent junior or senior instead of dimension of solution
+ Change some equations for large-scale optimization
+ Apply the ideas of levy-flight and global best
Hyper-parameters should fine tuned in approximate range to get faster convergen toward the global optimum:
+ pb (float): [0.1, 0.5], percent of the best (p in the paper), default = 0.1
+ kr (float): [0.5, 0.9], knowledge ratio, default = 0.7
Examples
~~~~~~~~
>>> import numpy as np
>>> from mealpy.human_based.GSKA import BaseGSKA
>>>
>>> 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
>>> pb = 0.1
>>> kr = 0.9
>>> model = BaseGSKA(problem_dict1, epoch, pop_size, pb, kr)
>>> best_position, best_fitness = model.solve()
>>> print(f"Solution: {best_position}, Fitness: {best_fitness}")
"""
def __init__(self, problem, epoch=10000, pop_size=100, pb=0.1, kr=0.7, **kwargs):
"""
Args:
problem (dict): The problem dictionary
epoch (int): maximum number of iterations, default = 10000
pop_size (int): number of population size, default = 100, n: pop_size, m: clusters
pb (float): percent of the best 0.1%, 0.8%, 0.1% (p in the paper), default = 0.1
kr (float): knowledge ratio, default = 0.7
"""
super().__init__(problem, kwargs)
self.nfe_per_epoch = pop_size
self.sort_flag = True
self.epoch = epoch
self.pop_size = pop_size
self.pb = pb
self.kr = kr
[docs] def evolve(self, epoch):
"""
The main operations (equations) of algorithm. Inherit from Optimizer class
Args:
epoch (int): The current iteration
"""
D = int(np.ceil(self.pop_size * (1 - (epoch + 1) / self.epoch)))
pop_new = []
for idx in range(0, self.pop_size):
# If it is the best it chooses best+2, best+1
if idx == 0:
previ, nexti = idx + 2, idx + 1
# If it is the worse it chooses worst-2, worst-1
elif idx == self.pop_size - 1:
previ, nexti = idx - 2, idx - 1
# Other case it chooses i-1, i+1
else:
previ, nexti = idx - 1, idx + 1
if idx < D: # senior gaining and sharing
if np.random.uniform() <= self.kr:
rand_idx = np.random.choice(list(set(range(0, self.pop_size)) - {previ, idx, nexti}))
if self.compare_agent(self.pop[rand_idx], self.pop[idx]):
pos_new = self.pop[idx][self.ID_POS] + np.random.uniform(0, 1, self.problem.n_dims) * \
(self.pop[previ][self.ID_POS] - self.pop[nexti][self.ID_POS] +
self.pop[rand_idx][self.ID_POS] - self.pop[idx][self.ID_POS])
else:
pos_new = self.g_best[self.ID_POS] + np.random.uniform(0, 1, self.problem.n_dims) * \
(self.pop[rand_idx][self.ID_POS] - self.pop[idx][self.ID_POS])
else:
pos_new = np.random.uniform(self.problem.lb, self.problem.ub)
else: # junior gaining and sharing
if np.random.uniform() <= self.kr:
id1 = int(self.pb * self.pop_size)
id2 = id1 + int(self.pop_size - 2 * 100 * self.pb)
rand_best = np.random.choice(list(set(range(0, id1)) - {idx}))
rand_worst = np.random.choice(list(set(range(id2, self.pop_size)) - {idx}))
rand_mid = np.random.choice(list(set(range(id1, id2)) - {idx}))
if self.compare_agent(self.pop[rand_mid], self.pop[idx]):
pos_new = self.pop[idx][self.ID_POS] + np.random.uniform(0, 1, self.problem.n_dims) * \
(self.pop[rand_best][self.ID_POS] - self.pop[rand_worst][self.ID_POS] +
self.pop[rand_mid][self.ID_POS] - self.pop[idx][self.ID_POS])
else:
pos_new = self.g_best[self.ID_POS] + np.random.uniform(0, 1, self.problem.n_dims) * \
(self.pop[rand_mid][self.ID_POS] - self.pop[idx][self.ID_POS])
else:
pos_new = np.random.uniform(self.problem.lb, self.problem.ub)
pos_new = self.amend_position(pos_new)
pop_new.append([pos_new, None])
self.pop = self.update_fitness_population(pop_new)
[docs]class OriginalGSKA(Optimizer):
"""
The original version of: Gaining Sharing Knowledge-based Algorithm (GSKA)
Links:
1. https://doi.org/10.1007/s13042-019-01053-x
Hyper-parameters should fine tuned in approximate range to get faster convergen toward the global optimum:
+ pb (float): [0.1, 0.5], percent of the best (p in the paper), default = 0.1
+ kf (float): [0.3, 0.8], knowledge factor that controls the total amount of gained and shared knowledge added from others to the current individual during generations, default = 0.5
+ kr (float): [0.5, 0.95], knowledge ratio, default = 0.9
+ kg (int): [3, 20], number of generations effect to D-dimension, default = 5
Examples
~~~~~~~~
>>> import numpy as np
>>> from mealpy.human_based.GSKA import OriginalGSKA
>>>
>>> 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
>>> pb = 0.1
>>> kf = 0.5
>>> kr = 0.9
>>> kg = 5
>>> model = OriginalGSKA(problem_dict1, epoch, pop_size, pb, kf, kr, kg)
>>> best_position, best_fitness = model.solve()
>>> print(f"Solution: {best_position}, Fitness: {best_fitness}")
References
~~~~~~~~~~
[1] Mohamed, A.W., Hadi, A.A. and Mohamed, A.K., 2020. Gaining-sharing knowledge based algorithm for solving
optimization problems: a novel nature-inspired algorithm. International Journal of Machine Learning and Cybernetics, 11(7), pp.1501-1529.
"""
def __init__(self, problem, epoch=10000, pop_size=100, pb=0.1, kf=0.5, kr=0.9, kg=5, **kwargs):
"""
Args:
problem (dict): The problem dictionary
epoch (int): maximum number of iterations, default = 10000
pop_size (int): number of population size, default = 100, n: pop_size, m: clusters
pb (float): percent of the best 0.1%, 0.8%, 0.1% (p in the paper), default = 0.1
kf (float): knowledge factor that controls the total amount of gained and shared knowledge added
from others to the current individual during generations, default = 0.5
kr (float): knowledge ratio, default = 0.9
kg (int): Number of generations effect to D-dimension, default = 5
"""
super().__init__(problem, kwargs)
self.nfe_per_epoch = pop_size
self.sort_flag = True
self.epoch = epoch
self.pop_size = pop_size
self.pb = pb
self.kf = kf
self.kr = kr
self.kg = kg
[docs] def evolve(self, epoch):
"""
The main operations (equations) of algorithm. Inherit from Optimizer class
Args:
epoch (int): The current iteration
"""
D = int(self.problem.n_dims * (1 - (epoch + 1) / self.epoch) ** self.kg)
pop_new = []
for idx in range(0, self.pop_size):
# If it is the best it chooses best+2, best+1
if idx == 0:
previ, nexti = idx + 2, idx + 1
# If it is the worse it chooses worst-2, worst-1
elif idx == self.pop_size - 1:
previ, nexti = idx - 2, idx - 1
# Other case it chooses i-1, i+1
else:
previ, nexti = idx - 1, idx + 1
# The random individual is for all dimension values
rand_idx = np.random.choice(list(set(range(0, self.pop_size)) - {previ, idx, nexti}))
pos_new = deepcopy(self.pop[idx][self.ID_POS])
for j in range(0, self.problem.n_dims):
if j < D: # junior gaining and sharing
if np.random.uniform() <= self.kr:
if self.compare_agent(self.pop[rand_idx], self.pop[idx]):
pos_new[j] = self.pop[idx][self.ID_POS][j] + self.kf * \
(self.pop[previ][self.ID_POS][j] - self.pop[nexti][self.ID_POS][j] +
self.pop[rand_idx][self.ID_POS][j] - self.pop[idx][self.ID_POS][j])
else:
pos_new[j] = self.pop[idx][self.ID_POS][j] + self.kf * \
(self.pop[previ][self.ID_POS][j] - self.pop[nexti][self.ID_POS][j] +
self.pop[idx][self.ID_POS][j] - self.pop[rand_idx][self.ID_POS][j])
else: # senior gaining and sharing
if np.random.uniform() <= self.kr:
id1 = int(self.pb * self.pop_size)
id2 = id1 + int(self.pop_size - 2 * 100 * self.pb)
rand_best = np.random.choice(list(set(range(0, id1)) - {idx}))
rand_worst = np.random.choice(list(set(range(id2, self.pop_size)) - {idx}))
rand_mid = np.random.choice(list(set(range(id1, id2)) - {idx}))
if self.compare_agent(self.pop[rand_mid], self.pop[idx]):
pos_new[j] = self.pop[idx][self.ID_POS][j] + self.kf * \
(self.pop[rand_best][self.ID_POS][j] - self.pop[rand_worst][self.ID_POS][j] +
self.pop[rand_mid][self.ID_POS][j] - self.pop[idx][self.ID_POS][j])
else:
pos_new[j] = self.pop[idx][self.ID_POS][j] + self.kf * \
(self.pop[rand_best][self.ID_POS][j] - self.pop[rand_worst][self.ID_POS][j] +
self.pop[idx][self.ID_POS][j] - self.pop[rand_mid][self.ID_POS][j])
pos_new = self.amend_position(pos_new)
pop_new.append([pos_new, None])
self.pop = self.update_fitness_population(pop_new)