# !/usr/bin/env python
# Created by "Thieu" at 19:27, 10/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 BaseEP(Optimizer):
"""
The original version of: Evolutionary Programming (EP)
Links:
1. http://www.cleveralgorithms.com/nature-inspired/evolution/evolutionary_programming.html
2. https://github.com/clever-algorithms/CleverAlgorithms
Hyper-parameters should fine tuned in approximate range to get faster convergen toward the global optimum:
+ bout_size (float/int): Number of tried with tournament selection (5% of pop_size)
+ if float number --> percentage of child agents, [0.05, 0.2]
+ int --> number of child agents, [3, 20]
Examples
~~~~~~~~
>>> import numpy as np
>>> from mealpy.evolutionary_based.EP import BaseEP
>>>
>>> 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
>>> bout_size = 0.05
>>> model = BaseEP(problem_dict1, epoch, pop_size, bout_size)
>>> best_position, best_fitness = model.solve()
>>> print(f"Solution: {best_position}, Fitness: {best_fitness}")
References
~~~~~~~~~~
[1] Yao, X., Liu, Y. and Lin, G., 1999. Evolutionary programming made faster.
IEEE Transactions on Evolutionary computation, 3(2), pp.82-102.
"""
ID_POS = 0
ID_TAR = 1
ID_STR = 2 # strategy
ID_WIN = 3
def __init__(self, problem, epoch=10000, pop_size=100, bout_size=0.05, **kwargs):
"""
Args:
problem (dict): The problem dictionary
epoch (int): maximum number of iterations, default = 10000
pop_size (int): number of population size (miu in the paper), default = 100
n_child (float/int): if float number --> percentage of child agents, int --> number of child agents
"""
super().__init__(problem, kwargs)
self.nfe_per_epoch = pop_size
self.sort_flag = True
self.epoch = epoch
self.pop_size = pop_size
if bout_size < 1: # Number of tried with tournament selection (5% of pop_size)
self.bout_size = int(bout_size * self.pop_size)
else:
self.bout_size = int(bout_size)
self.distance = 0.05 * (self.problem.ub - self.problem.lb)
[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, ...]], strategy, times_win]
"""
position = np.random.uniform(self.problem.lb, self.problem.ub)
position = self.amend_position(position)
fitness = self.get_fitness_position(position=position)
strategy = np.random.uniform(0, self.distance, self.problem.n_dims)
times_win = 0
return [position, fitness, strategy, times_win]
[docs] def evolve(self, epoch):
"""
The main operations (equations) of algorithm. Inherit from Optimizer class
Args:
epoch (int): The current iteration
"""
child = []
for idx in range(0, self.pop_size):
pos_new = self.pop[idx][self.ID_POS] + self.pop[idx][self.ID_STR] * np.random.normal(0, 1.0, self.problem.n_dims)
pos_new = self.amend_position(pos_new)
s_old = self.pop[idx][self.ID_STR] + np.random.normal(0, 1.0, self.problem.n_dims) * np.abs(self.pop[idx][self.ID_STR]) ** 0.5
child.append([pos_new, None, s_old, 0])
child = self.update_fitness_population(child)
# Update the global best
children, self.g_best = self.update_global_best_solution(child, save=False)
pop = children + self.pop
for i in range(0, len(pop)):
## Tournament winner (Tried with bout_size times)
for idx in range(0, self.bout_size):
rand_idx = np.random.randint(0, len(pop))
if self.compare_agent(pop[i], pop[rand_idx]):
pop[i][self.ID_WIN] += 1
else:
pop[rand_idx][self.ID_WIN] += 1
pop = sorted(pop, key=lambda item: item[self.ID_WIN], reverse=True)
self.pop = pop[:self.pop_size]
[docs]class LevyEP(BaseEP):
"""
My Levy-flight version of: Evolutionary Programming (LevyEP)
Notes
~~~~~
I try to apply Levy-flight to EP and change flow and add some equations.
Hyper-parameters should fine tuned in approximate range to get faster convergen toward the global optimum:
+ bout_size (float/int): Number of tried with tournament selection (5% of pop_size)
+ if float number --> percentage of child agents, [0.05, 0.2]
+ int --> number of child agents, [3, 20]
Examples
~~~~~~~~
>>> import numpy as np
>>> from mealpy.evolutionary_based.EP import LevyEP
>>>
>>> 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
>>> bout_size = 0.05
>>> model = LevyEP(problem_dict1, epoch, pop_size, bout_size)
>>> best_position, best_fitness = model.solve()
>>> print(f"Solution: {best_position}, Fitness: {best_fitness}")
"""
def __init__(self, problem, epoch=10000, pop_size=100, bout_size=0.05, **kwargs):
"""
Args:
problem (dict): The problem dictionary
epoch (int): maximum number of iterations, default = 10000
pop_size (int): number of population size (miu in the paper), default = 100
bout_size (float/int): if float number --> percentage of child agents, int --> number of child agents
"""
super().__init__(problem, epoch, pop_size, bout_size, **kwargs)
self.nfe_per_epoch = 2 * pop_size
self.sort_flag = True
[docs] def evolve(self, epoch):
"""
The main operations (equations) of algorithm. Inherit from Optimizer class
Args:
epoch (int): The current iteration
"""
child = []
for idx in range(0, self.pop_size):
pos_new = self.pop[idx][self.ID_POS] + self.pop[idx][self.ID_STR] * np.random.normal(0, 1.0, self.problem.n_dims)
pos_new = self.amend_position(pos_new)
s_old = self.pop[idx][self.ID_STR] + np.random.normal(0, 1.0, self.problem.n_dims) * np.abs(self.pop[idx][self.ID_STR]) ** 0.5
child.append([pos_new, None, s_old, 0])
child = self.update_fitness_population(child)
# Update the global best
children, self.g_best = self.update_global_best_solution(child, save=False)
pop = children + self.pop
for i in range(0, len(pop)):
## Tournament winner (Tried with bout_size times)
for idx in range(0, self.bout_size):
rand_idx = np.random.randint(0, len(pop))
if self.compare_agent(pop[i], pop[rand_idx]):
pop[i][self.ID_WIN] += 1
else:
pop[rand_idx][self.ID_WIN] += 1
## Keep the top population, but 50% of left population will make a comeback an take the good position
pop = sorted(pop, key=lambda agent: agent[self.ID_WIN], reverse=True)
pop = deepcopy(pop[:self.pop_size])
pop_left = deepcopy(pop[self.pop_size:])
## Choice random 50% of population left
pop_comeback = []
idx_list = np.random.choice(range(0, len(pop_left)), int(0.5 * len(pop_left)), replace=False)
for idx in idx_list:
levy = self.get_levy_flight_step(multiplier=0.001, case=-1)
pos_new = pop_left[idx][self.ID_POS] + 0.01 * levy
strategy = self.distance = 0.05 * (self.problem.ub - self.problem.lb)
pop_comeback.append([pos_new, None, strategy, 0])
pop_comeback = self.update_fitness_population(pop_comeback)
self.nfe_per_epoch = self.pop_size + int(0.5 * len(pop_left))
self.pop = self.get_sorted_strim_population(pop + pop_comeback, self.pop_size)