#!/usr/bin/env python
# Created by "Thieu" at 09:49, 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 OriginalPSO(Optimizer):
"""
The original version of: Particle Swarm Optimization (PSO)
Hyper-parameters should fine-tune in approximate range to get faster convergence toward the global optimum:
+ c1 (float): [1, 3], local coefficient, default = 2.05
+ c2 (float): [1, 3], global coefficient, default = 2.05
+ w (float): (0., 1.0), Weight min of bird, default = 0.4
Examples
~~~~~~~~
>>> import numpy as np
>>> from mealpy import FloatVar, PSO
>>>
>>> def objective_function(solution):
>>> return np.sum(solution**2)
>>>
>>> problem_dict = {
>>> "bounds": FloatVar(n_vars=30, lb=(-10.,) * 30, ub=(10.,) * 30, name="delta"),
>>> "obj_func": objective_function,
>>> "minmax": "min",
>>> }
>>>
>>> model = PSO.OriginalPSO(epoch=1000, pop_size=50, c1=2.05, c2=20.5, w=0.4)
>>> 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] Kennedy, J. and Eberhart, R., 1995, November. Particle swarm optimization. In Proceedings of
ICNN'95-international conference on neural networks (Vol. 4, pp. 1942-1948). IEEE.
"""
def __init__(self, epoch: int = 10000, pop_size: int = 100, c1: float = 2.05, c2: float = 2.05, w: float = 0.4, **kwargs: object) -> None:
"""
Args:
epoch: maximum number of iterations, default = 10000
pop_size: number of population size, default = 100
c1: [0-2] local coefficient
c2: [0-2] global coefficient
w_min: Weight min of bird, default = 0.4
"""
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.c1 = self.validator.check_float("c1", c1, (0, 5.0))
self.c2 = self.validator.check_float("c2", c2, (0, 5.0))
self.w = self.validator.check_float("w", w, (0, 1.0))
self.set_parameters(["epoch", "pop_size", "c1", "c2", "w"])
self.sort_flag = False
self.is_parallelizable = False
[docs] def initialize_variables(self):
self.v_max = 0.5 * (self.problem.ub - self.problem.lb)
self.v_min = -self.v_max
[docs] def generate_empty_agent(self, solution: np.ndarray = None) -> Agent:
if solution is None:
solution = self.problem.generate_solution(encoded=True)
velocity = self.generator.uniform(self.v_min, self.v_max)
local_pos = solution.copy()
return Agent(solution=solution, velocity=velocity, local_solution=local_pos)
[docs] def generate_agent(self, solution: np.ndarray = None) -> Agent:
agent = self.generate_empty_agent(solution)
agent.target = self.get_target(agent.solution)
agent.local_target = agent.target.copy()
return agent
[docs] def amend_solution(self, solution: np.ndarray) -> np.ndarray:
condition = np.logical_and(self.problem.lb <= solution, solution <= self.problem.ub)
pos_rand = self.generator.uniform(self.problem.lb, self.problem.ub)
return np.where(condition, solution, pos_rand)
[docs] def evolve(self, epoch):
"""
The main operations (equations) of algorithm. Inherit from Optimizer class
Args:
epoch (int): The current iteration
"""
# Update weight after each move count (weight down)
for idx in range(0, self.pop_size):
cognitive = self.c1 * self.generator.random(self.problem.n_dims) * (self.pop[idx].local_solution - self.pop[idx].solution)
social = self.c2 * self.generator.random(self.problem.n_dims) * (self.g_best.solution - self.pop[idx].solution)
self.pop[idx].velocity = self.w * self.pop[idx].velocity + cognitive + social
pos_new = self.pop[idx].solution + self.pop[idx].velocity
pos_new = self.correct_solution(pos_new)
target = self.get_target(pos_new)
if self.compare_target(target, self.pop[idx].target, self.problem.minmax):
self.pop[idx].update(solution=pos_new.copy(), target=target.copy())
if self.compare_target(target, self.pop[idx].local_target, self.problem.minmax):
self.pop[idx].update(local_solution=pos_new.copy(), local_target=target.copy())
[docs]class AIW_PSO(Optimizer):
"""
The original version of: Adaptive Inertia Weight Particle Swarm Optimization (AIW-PSO)
Hyper-parameters should fine-tune in approximate range to get faster convergence toward the global optimum:
+ c1 (float): [1, 3], local coefficient, default = 2.05
+ c2 (float): [1, 3], global coefficient, default = 2.05
+ alpha (float): [0., 1.0], The positive constant, default = 0.4
Examples
~~~~~~~~
>>> import numpy as np
>>> from mealpy import FloatVar, PSO
>>>
>>> def objective_function(solution):
>>> return np.sum(solution**2)
>>>
>>> problem_dict = {
>>> "bounds": FloatVar(n_vars=30, lb=(-10.,) * 30, ub=(10.,) * 30, name="delta"),
>>> "obj_func": objective_function,
>>> "minmax": "min",
>>> }
>>>
>>> model = PSO.AIW_PSO(epoch=1000, pop_size=50, c1=2.05, c2=20.5, alpha=0.4)
>>> 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] Qin, Z., Yu, F., Shi, Z., Wang, Y. (2006). Adaptive Inertia Weight Particle Swarm Optimization. In: Rutkowski, L.,
Tadeusiewicz, R., Zadeh, L.A., Żurada, J.M. (eds) Artificial Intelligence and Soft Computing – ICAISC 2006. ICAISC 2006.
Lecture Notes in Computer Science(), vol 4029. Springer, Berlin, Heidelberg. https://doi.org/10.1007/11785231_48
"""
def __init__(self, epoch: int = 10000, pop_size: int = 100, c1: float = 2.05, c2: float = 2.05, alpha: float = 0.4, **kwargs: object) -> None:
"""
Args:
epoch: maximum number of iterations, default = 10000
pop_size: number of population size, default = 100
c1: [0-2] local coefficient
c2: [0-2] global coefficient
alpha: The positive constant, default = 0.4
"""
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.c1 = self.validator.check_float("c1", c1, (0, 5.0))
self.c2 = self.validator.check_float("c2", c2, (0, 5.0))
self.alpha = self.validator.check_float("alpha", alpha, [0., 1.0])
self.set_parameters(["epoch", "pop_size", "c1", "c2", "alpha"])
self.sort_flag = False
self.is_parallelizable = False
[docs] def initialize_variables(self):
self.v_max = 0.5 * (self.problem.ub - self.problem.lb)
self.v_min = -self.v_max
[docs] def generate_empty_agent(self, solution: np.ndarray = None) -> Agent:
if solution is None:
solution = self.problem.generate_solution(encoded=True)
velocity = self.generator.uniform(self.v_min, self.v_max)
local_pos = solution.copy()
return Agent(solution=solution, velocity=velocity, local_solution=local_pos)
[docs] def generate_agent(self, solution: np.ndarray = None) -> Agent:
agent = self.generate_empty_agent(solution)
agent.target = self.get_target(agent.solution)
agent.local_target = agent.target.copy()
return agent
[docs] def amend_solution(self, solution: np.ndarray) -> np.ndarray:
condition = np.logical_and(self.problem.lb <= solution, solution <= self.problem.ub)
pos_rand = self.generator.uniform(self.problem.lb, self.problem.ub)
return np.where(condition, solution, pos_rand)
[docs] def evolve(self, epoch):
"""
The main operations (equations) of algorithm. Inherit from Optimizer class
Args:
epoch (int): The current iteration
"""
current_best = self.get_best_agent(self.pop, self.problem.minmax)
for idx in range(0, self.pop_size):
denom = np.abs(self.pop[idx].local_solution - current_best.solution)
denom = np.where(denom==0, 1e-6, denom)
isa = np.abs(self.pop[idx].solution - self.pop[idx].local_solution) / denom # individual search ability
w = 1 - self.alpha * (1.0 / (1.0 + np.exp(-isa)))
cognitive = self.c1 * self.generator.random(self.problem.n_dims) * (self.pop[idx].local_solution - self.pop[idx].solution)
social = self.c2 * self.generator.random(self.problem.n_dims) * (self.g_best.solution - self.pop[idx].solution)
velocity = w * self.pop[idx].velocity + cognitive + social
self.pop[idx].velocity = np.clip(velocity, self.v_min, self.v_max)
pos_new = self.pop[idx].solution + self.pop[idx].velocity
pos_new = self.correct_solution(pos_new)
target = self.get_target(pos_new)
if self.compare_target(target, self.pop[idx].target, self.problem.minmax):
self.pop[idx].update(solution=pos_new.copy(), target=target.copy())
if self.compare_target(target, self.pop[idx].local_target, self.problem.minmax):
self.pop[idx].update(local_solution=pos_new.copy(), local_target=target.copy())
[docs]class LDW_PSO(Optimizer):
"""
The original version of: Linearly Decreasing inertia Weight Particle Swarm Optimization (LDW-PSO)
Hyper-parameters should fine-tune in approximate range to get faster convergence toward the global optimum:
+ c1 (float): [1, 3], local coefficient, default = 2.05
+ c2 (float): [1, 3], global coefficient, default = 2.05
+ w_min (float): [0.1, 0.5], Weight min of bird, default = 0.4
+ w_max (float): [0.8, 2.0], Weight max of bird, default = 0.9
Examples
~~~~~~~~
>>> import numpy as np
>>> from mealpy import FloatVar, PSO
>>>
>>> def objective_function(solution):
>>> return np.sum(solution**2)
>>>
>>> problem_dict = {
>>> "bounds": FloatVar(n_vars=30, lb=(-10.,) * 30, ub=(10.,) * 30, name="delta"),
>>> "obj_func": objective_function,
>>> "minmax": "min",
>>> }
>>>
>>> model = PSO.LDW_PSO(epoch=1000, pop_size=50, c1=2.05, c2=20.5, w_min=0.4, w_max=0.9)
>>> 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] Shi, Yuhui, and Russell Eberhart. "A modified particle swarm optimizer." In 1998 IEEE international conference on
evolutionary computation proceedings. IEEE world congress on computational intelligence (Cat. No. 98TH8360), pp. 69-73. IEEE, 1998.
"""
def __init__(self, epoch: int = 10000, pop_size: int = 100, c1: float = 2.05, c2: float = 2.05,
w_min: float = 0.4, w_max: float = 0.9, **kwargs: object) -> None:
"""
Args:
epoch: maximum number of iterations, default = 10000
pop_size: number of population size, default = 100
c1: [0-2] local coefficient
c2: [0-2] global coefficient
w_min: Weight min of bird, default = 0.4
w_max: Weight max of bird, default = 0.9
"""
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.c1 = self.validator.check_float("c1", c1, (0, 5.0))
self.c2 = self.validator.check_float("c2", c2, (0, 5.0))
self.w_min = self.validator.check_float("w_min", w_min, (0, 0.5))
self.w_max = self.validator.check_float("w_max", w_max, [0.5, 2.0])
self.set_parameters(["epoch", "pop_size", "c1", "c2", "w_min", "w_max"])
self.sort_flag = False
self.is_parallelizable = False
[docs] def initialize_variables(self):
self.v_max = 0.5 * (self.problem.ub - self.problem.lb)
self.v_min = -self.v_max
[docs] def generate_empty_agent(self, solution: np.ndarray = None) -> Agent:
if solution is None:
solution = self.problem.generate_solution(encoded=True)
velocity = self.generator.uniform(self.v_min, self.v_max)
local_pos = solution.copy()
return Agent(solution=solution, velocity=velocity, local_solution=local_pos)
[docs] def generate_agent(self, solution: np.ndarray = None) -> Agent:
agent = self.generate_empty_agent(solution)
agent.target = self.get_target(agent.solution)
agent.local_target = agent.target.copy()
return agent
[docs] def amend_solution(self, solution: np.ndarray) -> np.ndarray:
condition = np.logical_and(self.problem.lb <= solution, solution <= self.problem.ub)
pos_rand = self.generator.uniform(self.problem.lb, self.problem.ub)
return np.where(condition, solution, pos_rand)
[docs] def evolve(self, epoch):
"""
The main operations (equations) of algorithm. Inherit from Optimizer class
Args:
epoch (int): The current iteration
"""
# Update weight after each move count (weight down)
w = (self.epoch - epoch) / self.epoch * (self.w_max - self.w_min) + self.w_min
for idx in range(0, self.pop_size):
cognitive = self.c1 * self.generator.random(self.problem.n_dims) * (self.pop[idx].local_solution - self.pop[idx].solution)
social = self.c2 * self.generator.random(self.problem.n_dims) * (self.g_best.solution - self.pop[idx].solution)
velocity = w * self.pop[idx].velocity + cognitive + social
self.pop[idx].velocity = np.clip(velocity, self.v_min, self.v_max)
pos_new = self.pop[idx].solution + self.pop[idx].velocity
pos_new = self.correct_solution(pos_new)
target = self.get_target(pos_new)
if self.compare_target(target, self.pop[idx].target, self.problem.minmax):
self.pop[idx].update(solution=pos_new.copy(), target=target.copy())
if self.compare_target(target, self.pop[idx].local_target, self.problem.minmax):
self.pop[idx].update(local_solution=pos_new.copy(), local_target=target.copy())
[docs]class P_PSO(Optimizer):
"""
The original version of: Phasor Particle Swarm Optimization (P-PSO)
Examples
~~~~~~~~
>>> import numpy as np
>>> from mealpy import FloatVar, PSO
>>>
>>> def objective_function(solution):
>>> return np.sum(solution**2)
>>>
>>> problem_dict = {
>>> "bounds": FloatVar(n_vars=30, lb=(-10.,) * 30, ub=(10.,) * 30, name="delta"),
>>> "obj_func": objective_function,
>>> "minmax": "min",
>>> }
>>>
>>> model = PSO.P_PSO(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] Ghasemi, M., Akbari, E., Rahimnejad, A., Razavi, S.E., Ghavidel, S. and Li, L., 2019.
Phasor particle swarm optimization: a simple and efficient variant of PSO. Soft Computing, 23(19), pp.9701-9718.
"""
def __init__(self, epoch: int = 10000, pop_size: int = 100, **kwargs: object) -> None:
"""
Args:
epoch: maximum number of iterations, default = 10000
pop_size: 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.is_parallelizable = False
[docs] def initialize_variables(self):
self.v_max = 0.5 * (self.problem.ub - self.problem.lb)
self.dyn_delta_list = self.generator.uniform(0, 2 * np.pi, self.pop_size)
[docs] def generate_empty_agent(self, solution: np.ndarray = None) -> Agent:
if solution is None:
solution = self.problem.generate_solution(encoded=True)
velocity = self.generator.uniform(-self.v_max, self.v_max)
local_pos = solution.copy()
return Agent(solution=solution, velocity=velocity, local_solution=local_pos)
[docs] def generate_agent(self, solution: np.ndarray = None) -> Agent:
agent = self.generate_empty_agent(solution)
agent.target = self.get_target(agent.solution)
agent.local_target = agent.target.copy()
return agent
[docs] def evolve(self, epoch):
"""
The main operations (equations) of algorithm. Inherit from Optimizer class
Args:
epoch (int): The current iteration
"""
for idx in range(0, self.pop_size):
aa = 2 * (np.sin(self.dyn_delta_list[idx]))
bb = 2 * (np.cos(self.dyn_delta_list[idx]))
ee = np.abs(np.cos(self.dyn_delta_list[idx])) ** aa
tt = np.abs(np.sin(self.dyn_delta_list[idx])) ** bb
v_new = ee * (self.pop[idx].local_solution - self.pop[idx].solution) + tt * (self.g_best.solution - self.pop[idx].solution)
v_new = np.minimum(np.maximum(v_new, -self.v_max), self.v_max)
self.pop[idx].velocity = v_new
pos_new = self.pop[idx].solution + v_new
pos_new = self.correct_solution(pos_new)
self.dyn_delta_list[idx] += np.abs(aa + bb) * (2 * np.pi)
self.v_max = (np.abs(np.cos(self.dyn_delta_list[idx])) ** 2) * (self.problem.ub - self.problem.lb)
target = self.get_target(pos_new)
if self.compare_target(target, self.pop[idx].target, self.problem.minmax):
self.pop[idx].update(solution=pos_new.copy(), target=target.copy())
if self.compare_target(target, self.pop[idx].local_target, self.problem.minmax):
self.pop[idx].update(local_solution=pos_new.copy(), local_target=target.copy())
[docs]class HPSO_TVAC(P_PSO):
"""
The original version of: Hierarchical PSO Time-Varying Acceleration (HPSO-TVAC)
Hyper-parameters should fine-tune in approximate range to get faster convergence toward the global optimum:
+ ci (float): [0.3, 1.0], c initial, default = 0.5
+ cf (float): [0.0, 0.3], c final, default = 0.1
Examples
~~~~~~~~
>>> import numpy as np
>>> from mealpy import FloatVar, PSO
>>>
>>> def objective_function(solution):
>>> return np.sum(solution**2)
>>>
>>> problem_dict = {
>>> "bounds": FloatVar(n_vars=30, lb=(-10.,) * 30, ub=(10.,) * 30, name="delta"),
>>> "obj_func": objective_function,
>>> "minmax": "min",
>>> }
>>>
>>> model = PSO.HPSO_TVAC(epoch=1000, pop_size=50, ci=0.5, cf=0.1)
>>> 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] Ghasemi, M., Aghaei, J. and Hadipour, M., 2017. New self-organising hierarchical PSO with
jumping time-varying acceleration coefficients. Electronics Letters, 53(20), pp.1360-1362.
"""
def __init__(self, epoch=10000, pop_size=100, ci=0.5, cf=0.1, **kwargs):
"""
Args:
epoch: maximum number of iterations, default = 10000
pop_size: number of population size, default = 100
ci: c initial, default = 0.5
cf: c final, default = 0.0
"""
super().__init__(epoch, pop_size, **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.ci = self.validator.check_float("ci", ci, [0.3, 1.0])
self.cf = self.validator.check_float("cf", cf, [0, 0.3])
self.set_parameters(["epoch", "pop_size", "ci", "cf"])
self.sort_flag = False
self.is_parallelizable = False
[docs] def evolve(self, epoch):
"""
The main operations (equations) of algorithm. Inherit from Optimizer class
Args:
epoch (int): The current iteration
"""
c_it = ((self.cf - self.ci) * ((epoch + 1) / self.epoch)) + self.ci
for idx in range(0, self.pop_size):
idx_k = self.generator.integers(0, self.pop_size)
w = self.generator.normal()
while np.abs(w - 1.0) < 0.01:
w = self.generator.normal()
c1_it = np.abs(w) ** (c_it * w)
c2_it = np.abs(1 - w) ** (c_it / (1 - w))
#################### HPSO
v_new = c1_it * self.generator.uniform(0, 1, self.problem.n_dims) * (self.pop[idx].local_solution - self.pop[idx].solution) + \
c2_it * self.generator.uniform(0, 1, self.problem.n_dims) * \
(self.g_best.solution + self.pop[idx_k].local_solution - 2 * self.pop[idx].solution)
v_new = np.where(v_new == 0, np.sign(0.5 - self.generator.uniform()) * self.generator.uniform() * self.v_max, v_new)
v_new = np.sign(v_new) * np.minimum(np.abs(v_new), self.v_max)
#########################
v_new = np.minimum(np.maximum(v_new, -self.v_max), self.v_max)
pos_new = self.pop[idx].solution + v_new
pos_new = self.correct_solution(pos_new)
self.pop[idx].velocity = v_new
target = self.get_target(pos_new)
if self.compare_target(target, self.pop[idx].target, self.problem.minmax):
self.pop[idx].update(solution=pos_new.copy(), target=target.copy())
if self.compare_target(target, self.pop[idx].local_target, self.problem.minmax):
self.pop[idx].update(local_solution=pos_new.copy(), local_target=target.copy())
[docs]class C_PSO(P_PSO):
"""
The original version of: Chaos Particle Swarm Optimization (C-PSO)
Hyper-parameters should fine-tune in approximate range to get faster convergence toward the global optimum:
+ c1 (float): [1.0, 3.0] local coefficient, default = 2.05
+ c2 (float): [1.0, 3.0] global coefficient, default = 2.05
+ w_min (float): [0.1, 0.4], Weight min of bird, default = 0.4
+ w_max (float): [0.4, 2.0], Weight max of bird, default = 0.9
Examples
~~~~~~~~
>>> import numpy as np
>>> from mealpy import FloatVar, PSO
>>>
>>> def objective_function(solution):
>>> return np.sum(solution**2)
>>>
>>> problem_dict = {
>>> "bounds": FloatVar(n_vars=30, lb=(-10.,) * 30, ub=(10.,) * 30, name="delta"),
>>> "obj_func": objective_function,
>>> "minmax": "min",
>>> }
>>>
>>> model = PSO.C_PSO(epoch=1000, pop_size=50, c1=2.05, c2=2.05, w_min=0.4, w_max=0.9)
>>> 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] Liu, B., Wang, L., Jin, Y.H., Tang, F. and Huang, D.X., 2005. Improved particle swarm optimization
combined with chaos. Chaos, Solitons & Fractals, 25(5), pp.1261-1271.
"""
def __init__(self, epoch: int = 10000, pop_size: int = 100, c1: float = 2.05, c2: float = 2.05,
w_min: float = 0.4, w_max: float = 0.9, **kwargs: object) -> None:
"""
Args:
epoch: maximum number of iterations, default = 10000
pop_size: number of population size, default = 100
c1: [0-2] local coefficient, default = 2.05
c2: [0-2] global coefficient, default = 2.05
w_min: Weight min of bird, default = 0.4
w_max: Weight max of bird, default = 0.9
"""
super().__init__(epoch, pop_size, **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.c1 = self.validator.check_float("c1", c1, (0, 5.0))
self.c2 = self.validator.check_float("c2", c2, (0, 5.0))
self.w_min = self.validator.check_float("w_min", w_min, (0, 0.5))
self.w_max = self.validator.check_float("w_max", w_max, [0.5, 2.0])
self.set_parameters(["epoch", "pop_size", "c1", "c2", "w_min", "w_max"])
self.sort_flag = False
self.is_parallelizable = False
[docs] def initialize_variables(self):
self.v_max = 0.5 * (self.problem.ub - self.problem.lb)
self.v_min = -self.v_max
self.N_CLS = int(self.pop_size / 5) # Number of chaotic local searches
self.dyn_lb = self.problem.lb.copy()
self.dyn_ub = self.problem.ub.copy()
[docs] def get_weights__(self, fit, fit_avg, fit_min):
temp1 = self.w_min + (self.w_max - self.w_min) * (fit - fit_min) / (fit_avg - fit_min)
if self.problem.minmax == "min":
output = temp1 if fit <= fit_avg else self.w_max
else:
output = self.w_max if fit <= fit_avg else temp1
return output
[docs] def bounded_solution(self, solution: np.ndarray) -> np.ndarray:
return np.clip(solution, self.dyn_lb, self.dyn_ub)
[docs] def evolve(self, epoch):
"""
The main operations (equations) of algorithm. Inherit from Optimizer class
Args:
epoch (int): The current iteration
"""
list_fits = [agent.target.fitness for agent in self.pop]
fit_avg = np.mean(list_fits)
fit_min = np.min(list_fits)
for idx in range(self.pop_size):
w = self.get_weights__(self.pop[idx].target.fitness, fit_avg, fit_min)
v_new = w * self.pop[idx].velocity + self.c1 * self.generator.random() * (self.pop[idx].local_solution - self.pop[idx].solution) + \
self.c2 * self.generator.random() * (self.g_best.solution - self.pop[idx].solution)
v_new = np.clip(v_new, self.v_min, self.v_max)
x_new = self.pop[idx].solution + v_new
self.pop[idx].velocity = v_new
pos_new = self.bounded_solution(x_new)
pos_new = self.correct_solution(pos_new)
target = self.get_target(pos_new)
if self.compare_target(target, self.pop[idx].target, self.problem.minmax):
self.pop[idx].update(solution=pos_new.copy(), target=target.copy())
if self.compare_target(target, self.pop[idx].local_target, self.problem.minmax):
self.pop[idx].update(local_solution=pos_new.copy(), local_target=target.copy())
## Implement chaostic local search for the best solution
g_best = self.g_best.copy()
cx_best_0 = (self.g_best.solution - self.problem.lb) / (self.problem.ub - self.problem.lb) # Eq. 7
cx_best_1 = 4 * cx_best_0 * (1 - cx_best_0) # Eq. 6
x_best = self.problem.lb + cx_best_1 * (self.problem.ub - self.problem.lb) # Eq. 8
x_best = self.correct_solution(x_best)
target_best = self.get_target(x_best)
if self.compare_target(target_best, self.g_best.target):
g_best.update(solution=x_best, target=target_best)
r = self.generator.random()
bound_min = np.stack([self.dyn_lb, g_best.solution - r * (self.dyn_ub - self.dyn_lb)])
self.dyn_lb = np.max(bound_min, axis=0)
bound_max = np.stack([self.dyn_ub, g_best.solution + r * (self.dyn_ub - self.dyn_lb)])
self.dyn_ub = np.min(bound_max, axis=0)
pop_new_child = self.generate_population(self.pop_size - self.N_CLS)
self.pop = self.get_sorted_and_trimmed_population(self.pop + pop_new_child, self.pop_size, self.problem.minmax)
[docs]class CL_PSO(Optimizer):
"""
The original version of: Comprehensive Learning Particle Swarm Optimization (CL-PSO)
Hyper-parameters should fine-tune in approximate range to get faster convergence toward the global optimum:
+ c_local (float): [1.0, 3.0], local coefficient, default = 1.2
+ w_min (float): [0.1, 0.5], Weight min of bird, default = 0.4
+ w_max (float): [0.7, 2.0], Weight max of bird, default = 0.9
+ max_flag (int): [5, 20], Number of times, default = 7
Examples
~~~~~~~~
>>> import numpy as np
>>> from mealpy import FloatVar, PSO
>>>
>>> def objective_function(solution):
>>> return np.sum(solution**2)
>>>
>>> problem_dict = {
>>> "bounds": FloatVar(n_vars=30, lb=(-10.,) * 30, ub=(10.,) * 30, name="delta"),
>>> "obj_func": objective_function,
>>> "minmax": "min",
>>> }
>>>
>>> model = PSO.CL_PSO(epoch=1000, pop_size=50, c_local = 1.2, w_min=0.4, w_max=0.9, max_flag = 7)
>>> 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] Liang, J.J., Qin, A.K., Suganthan, P.N. and Baskar, S., 2006. Comprehensive learning particle swarm optimizer
for global optimization of multimodal functions. IEEE transactions on evolutionary computation, 10(3), pp.281-295.
"""
def __init__(self, epoch: int = 10000, pop_size: int = 100, c_local: float = 1.2,
w_min: float = 0.4, w_max: float = 0.9, max_flag: int = 7, **kwargs: object) -> None:
"""
Args:
epoch: maximum number of iterations, default = 10000
pop_size: number of population size, default = 100
c_local: local coefficient, default = 1.2
w_min: Weight min of bird, default = 0.4
w_max: Weight max of bird, default = 0.9
max_flag: Number of times, default = 7
"""
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.c_local = self.validator.check_float("c_local", c_local, (0, 5.0))
self.w_min = self.validator.check_float("w_min", w_min, (0, 0.5))
self.w_max = self.validator.check_float("w_max", w_max, [0.5, 2.0])
self.max_flag = self.validator.check_int("max_flag", max_flag, [2, 100])
self.set_parameters(["epoch", "pop_size", "c_local", "w_min", "w_max", "max_flag"])
self.sort_flag = False
self.is_parallelizable = False
[docs] def initialize_variables(self):
self.v_max = 0.5 * (self.problem.ub - self.problem.lb)
self.v_min = -self.v_max
self.flags = np.zeros(self.pop_size)
[docs] def generate_empty_agent(self, solution: np.ndarray = None) -> Agent:
if solution is None:
solution = self.problem.generate_solution(encoded=True)
velocity = self.generator.uniform(-self.v_max, self.v_max)
local_pos = solution.copy()
return Agent(solution=solution, velocity=velocity, local_solution=local_pos)
[docs] def generate_agent(self, solution: np.ndarray = None) -> Agent:
agent = self.generate_empty_agent(solution)
agent.target = self.get_target(agent.solution)
agent.local_target = agent.target.copy()
return agent
[docs] def evolve(self, epoch):
"""
The main operations (equations) of algorithm. Inherit from Optimizer class
Args:
epoch (int): The current iteration
"""
wk = self.w_max * (epoch / self.epoch) * (self.w_max - self.w_min)
for idx in range(0, self.pop_size):
pci = 0.05 + 0.45 * (np.exp(10 * (idx + 1) / self.pop_size) - 1) / (np.exp(10) - 1)
vec_new = self.pop[idx].velocity.copy()
for jdx in range(0, self.problem.n_dims):
if self.generator.random() > pci:
vj = wk * self.pop[idx].velocity[jdx] + self.c_local * self.generator.random() * \
(self.pop[idx].local_solution[jdx] - self.pop[idx].solution[jdx])
else:
id1, id2 = self.generator.choice(list(set(range(0, self.pop_size)) - {idx}), 2, replace=False)
if self.compare_target(self.pop[id1].target, self.pop[id2].target, self.problem.minmax):
vj = wk * self.pop[idx].velocity[jdx] + self.c_local * self.generator.random() * \
(self.pop[id1].local_solution[jdx] - self.pop[idx].solution[jdx])
else:
vj = wk * self.pop[idx].velocity[jdx] + self.c_local * self.generator.random() * \
(self.pop[id2].local_solution[jdx] - self.pop[idx].solution[jdx])
vec_new[jdx] = vj
vec_new = np.clip(vec_new, self.v_min, self.v_max)
pos_new = self.pop[idx].solution + vec_new
pos_new = self.correct_solution(pos_new)
self.pop[idx].velocity = vec_new
target = self.get_target(pos_new)
if self.compare_target(target, self.pop[idx].target, self.problem.minmax):
self.pop[idx].update(solution=pos_new.copy(), target=target.copy())
if self.compare_target(target, self.pop[idx].local_target, self.problem.minmax):
self.pop[idx].update(local_solution=pos_new.copy(), local_target=target.copy())
self.flags[idx] = 0
else:
self.flags[idx] += 1
if self.flags[idx] >= self.max_flag:
self.flags[idx] = 0