#!/usr/bin/env python
# Created by "Thieu" at 21:18, 17/03/2020 ----------%
# Email: nguyenthieu2102@gmail.com %
# Github: https://github.com/thieu1995 %
# --------------------------------------------------%
import numpy as np
from mealpy.optimizer import Optimizer
from mealpy.utils.agent import Agent
[docs]class OriginalTWO(Optimizer):
"""
The original version of: Tug of War Optimization (TWO)
Links:
1. https://www.researchgate.net/publication/332088054_Tug_of_War_Optimization_Algorithm
Examples
~~~~~~~~
>>> import numpy as np
>>> from mealpy import FloatVar, TWO
>>>
>>> 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 = TWO.OriginalTWO(epoch=1000, pop_size=50)
>>> 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] Kaveh, A., 2017. Tug of war optimization. In Advances in metaheuristic algorithms for
optimal design of structures (pp. 451-487). Springer, Cham.
"""
def __init__(self, epoch: int = 10000, pop_size: int = 100, **kwargs: object) -> None:
"""
Args:
epoch (int): maximum number of iterations, default = 10000
pop_size (int): number of population size, default = 100
"""
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.set_parameters(["epoch", "pop_size"])
self.sort_flag = False
self.muy_s = 1
self.muy_k = 1
self.delta_t = 1
self.alpha = 0.99
self.beta = 0.1
[docs] def initialization(self):
if self.pop is None:
self.pop = self.generate_population(self.pop_size)
self.pop = self.update_weight__(self.pop)
[docs] def generate_empty_agent(self, solution: np.ndarray = None) -> Agent:
"""
Generate new agent with solution
Args:
solution (np.ndarray): The solution
"""
if solution is None:
solution = self.problem.generate_solution(encoded=True)
return Agent(solution=solution, weight=0.0)
[docs] def update_weight__(self, teams):
list_fits = np.array([agent.target.fitness for agent in teams])
maxx, minn = np.max(list_fits), np.min(list_fits)
if maxx == minn:
list_fits = self.generator.uniform(0.0, 1.0, self.pop_size)
list_weights = np.exp(-(list_fits - maxx) / (maxx - minn))
list_weights = list_weights/np.sum(list_weights) + 0.1
for idx in range(self.pop_size):
teams[idx].weight = list_weights[idx]
return teams
[docs] def evolve(self, epoch):
"""
The main operations (equations) of algorithm. Inherit from Optimizer class
Args:
epoch (int): The current iteration
"""
pop_new = self.pop.copy()
for idx in range(self.pop_size):
pos_new = pop_new[idx].solution.copy().astype(float)
for jdx in range(self.pop_size):
if self.pop[idx].weight < self.pop[jdx].weight:
force = max(self.pop[idx].weight * self.muy_s, self.pop[jdx].weight * self.muy_s)
resultant_force = force - self.pop[idx].weight * self.muy_k
g = self.pop[jdx].solution - self.pop[idx].solution
acceleration = resultant_force * g / (self.pop[idx].weight * self.muy_k)
delta_x = 0.5 * acceleration + np.power(self.alpha, epoch) * self.beta * \
(self.problem.ub - self.problem.lb) * self.generator.normal(0, 1, self.problem.n_dims)
pos_new += delta_x
pop_new[idx].solution = pos_new
for idx in range(self.pop_size):
pos_new = pop_new[idx].solution.copy().astype(float)
for jdx in range(self.problem.n_dims):
if pos_new[jdx] < self.problem.lb[jdx] or pos_new[jdx] > self.problem.ub[jdx]:
if self.generator.random() <= 0.5:
pos_new[jdx] = self.g_best.solution[jdx] + self.generator.standard_normal() / epoch * (self.g_best.solution[jdx] - pos_new[jdx])
if pos_new[jdx] < self.problem.lb[jdx] or pos_new[jdx] > self.problem.ub[jdx]:
pos_new[jdx] = self.pop[idx].solution[jdx]
else:
if pos_new[jdx] < self.problem.lb[jdx]:
pos_new[jdx] = self.problem.lb[jdx]
if pos_new[jdx] > self.problem.ub[jdx]:
pos_new[jdx] = self.problem.ub[jdx]
pos_new = self.correct_solution(pos_new)
pop_new[idx].solution = pos_new
if self.mode not in self.AVAILABLE_MODES:
pop_new[idx].target = self.get_target(pos_new)
self.pop[idx] = self.get_better_agent(pop_new[idx], self.pop[idx], 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)
self.pop = self.update_weight__(self.pop)
[docs]class OppoTWO(OriginalTWO):
"""
The opossition-based learning version: Tug of War Optimization (OTWO)
Examples
~~~~~~~~
>>> import numpy as np
>>> from mealpy import FloatVar, TWO
>>>
>>> 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 = TWO.OppoTWO(epoch=1000, pop_size=50)
>>> 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}")
"""
def __init__(self, epoch: int = 10000, pop_size: int = 100, **kwargs: object) -> None:
"""
Args:
epoch (int): maximum number of iterations, default = 10000
pop_size (int): number of population size, default = 100
"""
super().__init__(epoch, pop_size, **kwargs)
[docs] def initialization(self):
if self.pop is None:
self.pop = self.generate_population(self.pop_size)
list_idx = self.generator.choice(range(0, self.pop_size), int(self.pop_size/2), replace=False)
pop_temp = [self.pop[list_idx[idx]] for idx in range(0, int(self.pop_size/2))]
pop_oppo = []
for idx in range(len(pop_temp)):
pos_opposite = self.problem.ub + self.problem.lb - pop_temp[idx].solution
pos_opposite = self.correct_solution(pos_opposite)
agent = self.generate_empty_agent(pos_opposite)
pop_oppo.append(agent)
if self.mode not in self.AVAILABLE_MODES:
pop_oppo[-1].target = self.get_target(pos_opposite)
pop_oppo = self.update_target_for_population(pop_oppo)
self.pop = pop_temp + pop_oppo
self.pop = self.update_weight__(self.pop)
[docs] def evolve(self, epoch):
"""
The main operations (equations) of algorithm. Inherit from Optimizer class
Args:
epoch (int): The current iteration
"""
## Apply force of others solution on each individual solution
pop_new = self.pop.copy()
for idx in range(self.pop_size):
pos_new = pop_new[idx].solution.copy().astype(float)
for jdx in range(self.pop_size):
if self.pop[idx].weight < self.pop[jdx].weight:
force = max(self.pop[idx].weight * self.muy_s, self.pop[jdx].weight * self.muy_s)
resultant_force = force - self.pop[idx].weight * self.muy_k
g = self.pop[jdx].solution - self.pop[idx].solution
acceleration = resultant_force * g / (self.pop[idx].weight * self.muy_k)
delta_x = 1 / 2 * acceleration + np.power(self.alpha, epoch) * self.beta * \
(self.problem.ub - self.problem.lb) * self.generator.normal(0, 1, self.problem.n_dims)
pos_new += delta_x
self.pop[idx].solution = pos_new
## Amend solution and update fitness value
for idx in range(self.pop_size):
pos_new = self.g_best.solution + self.generator.normal(0, 1, self.problem.n_dims) / (epoch) * (self.g_best.solution - pop_new[idx].solution)
conditions = np.logical_or(pop_new[idx].solution < self.problem.lb, pop_new[idx].solution > self.problem.ub)
conditions = np.logical_and(conditions, self.generator.random(self.problem.n_dims) < 0.5)
pos_new = np.where(conditions, pos_new, self.pop[idx].solution)
pop_new[idx].solution = self.correct_solution(pos_new)
if self.mode not in self.AVAILABLE_MODES:
pop_new[idx].target = self.get_target(pos_new)
self.pop[idx] = self.get_better_agent(pop_new[idx], self.pop[idx], 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)
## Opposition-based here
pop = []
for idx in range(self.pop_size):
C_op = self.generate_opposition_solution(self.pop[idx], self.g_best)
pos_new = self.correct_solution(C_op)
agent = self.generate_empty_agent(pos_new)
pop.append(agent)
if self.mode not in self.AVAILABLE_MODES:
agent.target = self.get_target(pos_new)
self.pop[idx] = self.get_better_agent(agent, self.pop[idx], self.problem.minmax)
if self.mode in self.AVAILABLE_MODES:
pop = self.update_target_for_population(pop)
self.pop = self.greedy_selection_population(self.pop, pop, self.problem.minmax)
self.pop = self.update_weight__(self.pop)
[docs]class LevyTWO(OriginalTWO):
"""
The Levy-flight version of: Tug of War Optimization (LevyTWO)
Examples
~~~~~~~~
>>> import numpy as np
>>> from mealpy import FloatVar, TWO
>>>
>>> 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 = TWO.LevyTWO(epoch=1000, pop_size=50)
>>> 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}")
"""
def __init__(self, epoch: int = 10000, pop_size: int = 100, **kwargs: object) -> None:
"""
Args:
epoch (int): maximum number of iterations, default = 10000
pop_size (int): number of population size, default = 100
"""
super().__init__(epoch, pop_size, **kwargs)
[docs] def evolve(self, epoch):
"""
The main operations (equations) of algorithm. Inherit from Optimizer class
Args:
epoch (int): The current iteration
"""
pop_new = self.pop.copy()
for idx in range(self.pop_size):
pos_new = self.pop[idx].solution.copy().astype(float)
for kdx in range(self.pop_size):
if self.pop[idx].weight < self.pop[kdx].weight:
force = max(self.pop[idx].weight * self.muy_s, self.pop[kdx].weight * self.muy_s)
resultant_force = force - self.pop[idx].weight * self.muy_k
g = self.pop[kdx].solution - self.pop[idx].solution
acceleration = resultant_force * g / (self.pop[idx].weight * self.muy_k)
delta_x = 1 / 2 * acceleration + np.power(self.alpha, epoch) * self.beta * \
(self.problem.ub - self.problem.lb) * self.generator.normal(0, 1, self.problem.n_dims)
pos_new +=delta_x
pop_new[idx].solution = pos_new
for idx in range(self.pop_size):
pos_new = self.pop[idx].solution.copy().astype(float)
for jdx in range(self.problem.n_dims):
if pos_new[jdx] < self.problem.lb[jdx] or pos_new[jdx] > self.problem.ub[jdx]:
if self.generator.random() <= 0.5:
pos_new[jdx] = self.g_best.solution[jdx] + self.generator.standard_normal() / epoch * (self.g_best.solution[jdx] - pos_new[jdx])
if pos_new[jdx] < self.problem.lb[jdx] or pos_new[jdx] > self.problem.ub[jdx]:
pos_new[jdx] = self.pop[idx].solution[jdx]
else:
if pos_new[jdx] < self.problem.lb[jdx]:
pos_new[jdx] = self.problem.lb[jdx]
if pos_new[jdx] > self.problem.ub[jdx]:
pos_new[jdx] = self.problem.ub[jdx]
pop_new[idx].solution = self.correct_solution(pos_new)
if self.mode not in self.AVAILABLE_MODES:
pop_new[idx].target = self.get_target(pos_new)
self.pop[idx] = self.get_better_agent(pop_new[idx], self.pop[idx], 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)
### Apply levy-flight here
for idx in range(self.pop_size):
## Chance for each agent to update using levy is 50%
if self.generator.random() < 0.5:
levy_step = self.get_levy_flight_step(beta=1.0, multiplier=0.01, size=self.problem.n_dims, case=-1)
pos_new = pop_new[idx].solution + levy_step
pos_new = self.correct_solution(pos_new)
agent = self.generate_agent(pos_new)
if self.compare_target(agent.target, pop_new[idx].target, self.problem.minmax):
pop_new[idx] = agent
self.pop = self.update_weight__(pop_new)
[docs]class EnhancedTWO(OppoTWO, LevyTWO):
"""
The original version of: Enhenced Tug of War Optimization (ETWO)
Links:
1. https://doi.org/10.1016/j.procs.2020.03.063
Examples
~~~~~~~~
>>> import numpy as np
>>> from mealpy import FloatVar, TWO
>>>
>>> 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 = TWO.EnhancedTWO(epoch=1000, pop_size=50)
>>> 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] Nguyen, T., Hoang, B., Nguyen, G. and Nguyen, B.M., 2020. A new workload prediction model using
extreme learning machine and enhanced tug of war optimization. Procedia Computer Science, 170, pp.362-369.
"""
def __init__(self, epoch: int = 10000, pop_size: int = 100, **kwargs: object) -> None:
"""
Args:
epoch (int): maximum number of iterations, default = 10000
pop_size (int): number of population size, default = 100
"""
super().__init__(epoch, pop_size, **kwargs)
[docs] def initialization(self):
if self.pop is None:
self.pop = self.generate_population(self.pop_size)
pop_oppo = self.pop.copy()
for idx in range(self.pop_size):
pos_opposite = self.problem.ub + self.problem.lb - self.pop[idx].solution
pos_new = self.correct_solution(pos_opposite)
pop_oppo[idx].solution = pos_new
if self.mode not in self.AVAILABLE_MODES:
pop_oppo[idx].target = self.get_target(pos_new)
pop_oppo = self.update_target_for_population(pop_oppo)
self.pop = self.get_sorted_and_trimmed_population(self.pop + pop_oppo, self.pop_size, self.problem.minmax)
self.pop = self.update_weight__(self.pop)
[docs] def evolve(self, epoch):
"""
The main operations (equations) of algorithm. Inherit from Optimizer class
Args:
epoch (int): The current iteration
"""
pop_new = self.pop.copy()
for idx in range(self.pop_size):
pos_new = self.pop[idx].solution.copy().astype(float)
for kdx in range(self.pop_size):
if self.pop[idx].weight < self.pop[kdx].weight:
force = max(self.pop[idx].weight * self.muy_s, self.pop[kdx].weight * self.muy_s)
resultant_force = force - self.pop[idx].weight * self.muy_k
g = self.pop[kdx].solution - self.pop[idx].solution
acceleration = resultant_force * g / (self.pop[idx].weight * self.muy_k)
delta_x = 1 / 2 * acceleration + np.power(self.alpha, epoch) * self.beta * \
(self.problem.ub - self.problem.lb) * self.generator.normal(0, 1, self.problem.n_dims)
pos_new += delta_x
pop_new[idx].solution = pos_new
for idx in range(self.pop_size):
pos_new = self.pop[idx].solution.copy().astype(float)
for jdx in range(self.problem.n_dims):
if pos_new[jdx] < self.problem.lb[jdx] or pos_new[jdx] > self.problem.ub[jdx]:
if self.generator.random() <= 0.5:
pos_new[jdx] = self.g_best.solution[jdx] + self.generator.standard_normal() / epoch * (self.g_best.solution[jdx] - pos_new[jdx])
if pos_new[jdx] < self.problem.lb[jdx] or pos_new[jdx] > self.problem.ub[jdx]:
pos_new[jdx] = self.pop[idx].solution[jdx]
else:
if pos_new[jdx] < self.problem.lb[jdx]:
pos_new[jdx] = self.problem.lb[jdx]
if pos_new[jdx] > self.problem.ub[jdx]:
pos_new[jdx] = self.problem.ub[jdx]
pop_new[idx].solution = self.correct_solution(pos_new)
if self.mode not in self.AVAILABLE_MODES:
pop_new[idx].target = self.get_target(pos_new)
self.pop[idx] = self.get_better_agent(pop_new[idx], self.pop[idx], 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)
for idx in range(self.pop_size):
C_op = self.generate_opposition_solution(pop_new[idx], self.g_best)
pos_new = self.correct_solution(C_op)
agent = self.generate_agent(pos_new)
if self.compare_target(agent.target, pop_new[idx].target, self.problem.minmax):
pop_new[idx] = agent
else:
levy_step = self.get_levy_flight_step(beta=1.0, multiplier=1.0, size=self.problem.n_dims, case=-1)
pos_new = pop_new[idx].solution + 1.0 / np.sqrt(epoch) * levy_step
pos_new = self.correct_solution(pos_new)
agent = self.generate_agent(pos_new)
if self.compare_target(agent.target, pop_new[idx].target, self.problem.minmax):
pop_new[idx] = agent
self.pop = self.update_weight__(pop_new)