#!/usr/bin/env python
# ------------------------------------------------------------------------------------------------------%
# Created by "Thieu Nguyen" at 12:00, 17/03/2020 %
# %
# Email: nguyenthieu2102@gmail.com %
# Homepage: https://www.researchgate.net/profile/Thieu_Nguyen6 %
# Github: https://github.com/thieu1995 %
# -------------------------------------------------------------------------------------------------------%
import numpy as np
from copy import deepcopy
from mealpy.optimizer import Optimizer
[docs]class OriginalBA(Optimizer):
"""
The original version of: Bat-inspired Algorithm (BA)
Notes
~~~~~
+ The value of A and r parameters are constant
Hyper-parameters should fine tuned in approximate range to get faster convergen toward the global optimum:
+ loudness (float): (1.0, 2.0), loudness, default = 0.8
+ pulse_rate (float): (0.15, 0.85), pulse rate / emission rate, default = 0.95
+ pulse_frequency (list): (pf_min, pf_max) -> ([0, 3], [5, 20]), pulse frequency, default = (0, 10)
Examples
~~~~~~~~
>>> import numpy as np
>>> from mealpy.swarm_based.BA import OriginalBA
>>>
>>> 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
>>> loudness = 0.8
>>> pulse_rate = 0.95
>>> pulse_frequency = [0, 10]
>>> model = OriginalBA(problem_dict1, epoch, pop_size, loudness, pulse_rate, pulse_frequency)
>>> best_position, best_fitness = model.solve()
>>> print(f"Solution: {best_position}, Fitness: {best_fitness}")
References
~~~~~~~~~~
[1] Yang, X.S., 2010. A new metaheuristic bat-inspired algorithm. In Nature inspired cooperative
strategies for optimization (NICSO 2010) (pp. 65-74). Springer, Berlin, Heidelberg.
"""
ID_VEC = 2 # Velocity
ID_PFRE = 3 # Pulse Frequency
def __init__(self, problem, epoch=10000, pop_size=100, loudness=0.8, pulse_rate=0.95, pulse_frequency=(0, 10), **kwargs):
"""
Args:
problem (dict): The problem dictionary
epoch (int): maximum number of iterations, default = 10000
pop_size (int): number of population size, default = 100
loudness (float): (A_min, A_max): loudness, default = 0.8
pulse_rate (float): (r_min, r_max): pulse rate / emission rate, default = 0.95
pulse_frequency (list): (pf_min, pf_max): pulse frequency, default = (0, 10)
"""
super().__init__(problem, kwargs)
self.nfe_per_epoch = pop_size
self.sort_flag = False
self.epoch = epoch
self.pop_size = pop_size
self.loudness = loudness
self.pulse_rate = pulse_rate
self.pulse_frequency = pulse_frequency
self.alpha = self.gamma = 0.9
[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, ...]], velocity, pulse_frequency]
"""
position = np.random.uniform(self.problem.lb, self.problem.ub)
position = self.amend_position(position)
fitness = self.get_fitness_position(position=position)
velocity = np.random.uniform(self.problem.lb, self.problem.ub)
pulse_frequency = self.pulse_frequency[0] + (self.pulse_frequency[1] - self.pulse_frequency[0]) * np.random.uniform()
return [position, fitness, velocity, pulse_frequency]
[docs] def evolve(self, epoch):
"""
The main operations (equations) of algorithm. Inherit from Optimizer class
Args:
epoch (int): The current iteration
"""
pop_new = []
for idx in range(0, self.pop_size):
agent = deepcopy(self.pop[idx])
agent[self.ID_VEC] = agent[self.ID_VEC] + self.pop[idx][self.ID_PFRE] * (self.pop[idx][self.ID_POS] - self.g_best[self.ID_POS])
x = self.pop[idx][self.ID_POS] + agent[self.ID_VEC]
## Local Search around g_best position
if np.random.uniform() > self.pulse_rate:
x = self.g_best[self.ID_POS] + 0.0001 * np.random.normal(self.problem.n_dims) # gauss
pos_new = self.amend_position(x)
agent[self.ID_POS] = pos_new
pop_new.append(agent)
pop_new = self.update_fitness_population(pop_new)
for idx in range(self.pop_size):
## Replace the old position by the new one when its has better fitness.
## and then update loudness and emission rate
if self.compare_agent(pop_new[idx], self.pop[idx]) and np.random.rand() < self.loudness:
self.pop[idx] = deepcopy(pop_new[idx])
[docs]class BaseBA(Optimizer):
"""
The original version of: Bat-inspired Algorithm (BA)
Notes
~~~~~
+ The value of A and r are changing after each iteration
Hyper-parameters should fine tuned in approximate range to get faster convergen toward the global optimum:
+ loudness (float): (A_min, A_max) -> ([0.5, 1.5], [1.0, 3.0]): loudness, default = (1.0, 2.0)
+ pulse_rate (float): (r_min, r_max) -> ([0.1, 0.5], [0.5, 0.95]), pulse rate / emission rate, default = (0.15, 0.85)
+ pulse_frequency (list): (pf_min, pf_max) -> ([0, 3], [5, 20]), pulse frequency, default = (0, 10)
Examples
~~~~~~~~
>>> import numpy as np
>>> from mealpy.swarm_based.BA import BaseBA
>>>
>>> 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
>>> loudness = [1.0, 2.0]
>>> pulse_rate = [0.15, 0.85]
>>> pulse_frequency = [0, 10]
>>> model = BaseBA(problem_dict1, epoch, pop_size, loudness, pulse_rate, pulse_frequency)
>>> best_position, best_fitness = model.solve()
>>> print(f"Solution: {best_position}, Fitness: {best_fitness}")
References
~~~~~~~~~~
[1] Yang, X.S., 2010. A new metaheuristic bat-inspired algorithm. In Nature inspired cooperative
strategies for optimization (NICSO 2010) (pp. 65-74). Springer, Berlin, Heidelberg.
"""
ID_VEC = 2 # Velocity
ID_LOUD = 3 # Loudness
ID_PRAT = 4 # Pulse Rate
ID_PFRE = 5 # Pulse Frequency
def __init__(self, problem, epoch=10000, pop_size=100, loudness=(1.0, 2.0),
pulse_rate=(0.15, 0.85), pulse_frequency=(0, 10), **kwargs):
"""
Args:
problem (dict): The problem dictionary
epoch (int): maximum number of iterations, default = 10000
pop_size (int): number of population size, default = 100
loudness (): (A_min, A_max): loudness, default = (1.0, 2.0)
pulse_rate (): (r_min, r_max): pulse rate / emission rate, default = (0.15, 0.85)
pulse_frequency (): (pf_min, pf_max): pulse frequency, default = (0, 10)
"""
super().__init__(problem, kwargs)
self.nfe_per_epoch = pop_size
self.sort_flag = False
self.epoch = epoch
self.pop_size = pop_size
self.loudness = loudness
self.pulse_rate = pulse_rate
self.pulse_frequency = pulse_frequency
self.alpha = self.gamma = 0.9
[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, ...]], velocity, loudness, pulse_rate, pulse_frequency]
"""
position = np.random.uniform(self.problem.lb, self.problem.ub)
position = self.amend_position(position)
fitness = self.get_fitness_position(position=position)
velocity = np.random.uniform(self.problem.lb, self.problem.ub)
loudness = np.random.uniform(self.loudness[0], self.loudness[1])
pulse_rate = np.random.uniform(self.pulse_rate[0], self.pulse_rate[1])
pulse_frequency = self.pulse_frequency[0] + (self.pulse_frequency[1] - self.pulse_frequency[0]) * np.random.uniform()
return [position, fitness, velocity, loudness, pulse_rate, pulse_frequency]
[docs] def evolve(self, epoch):
"""
The main operations (equations) of algorithm. Inherit from Optimizer class
Args:
epoch (int): The current iteration
"""
mean_a = np.mean([agent[self.ID_LOUD] for agent in self.pop])
pop_new = []
for idx in range(0, self.pop_size):
agent = deepcopy(self.pop[idx])
agent[self.ID_VEC] = agent[self.ID_VEC] + self.pop[idx][self.ID_PFRE] * (self.pop[idx][self.ID_POS] - self.g_best[self.ID_POS])
x = self.pop[idx][self.ID_POS] + agent[self.ID_VEC]
## Local Search around g_best position
if np.random.uniform() > agent[self.ID_PRAT]:
# print(f"{epoch}, {mean_a}, {self.dyn_r}")
x = self.g_best[self.ID_POS] + mean_a * np.random.normal(-1, 1)
pos_new = self.amend_position(x)
agent[self.ID_POS] = pos_new
pop_new.append(agent)
pop_new = self.update_fitness_population(pop_new)
for idx in range(0, self.pop_size):
## Replace the old position by the new one when its has better fitness.
## and then update loudness and emission rate
if self.compare_agent(pop_new[idx], self.pop[idx]) and np.random.rand() < pop_new[idx][self.ID_LOUD]:
pop_new[idx][self.ID_LOUD] = self.alpha * pop_new[idx][self.ID_LOUD]
pop_new[idx][self.ID_PRAT] = pop_new[idx][self.ID_PRAT] * (1 - np.exp(-self.gamma * (epoch + 1)))
self.pop[idx] = deepcopy(pop_new[idx])
[docs]class ModifiedBA(Optimizer):
"""
My modified version of: Bat-inspired Algorithm (MBA)
Notes
~~~~~
+ Removes A (loudness) parameter
+ Changed processes
+ 1st: We proceed exploration phase (using frequency)
+ 2nd: If new position has better fitness we replace the old position
+ 3rd: Otherwise, we proceed exploitation phase (using finding around the best position so far)
Hyper-parameters should fine tuned in approximate range to get faster convergen toward the global optimum:
+ pulse_rate (float): [0.7, 1.0], pulse rate / emission rate, default = 0.95
+ pulse_frequency (list): (pf_min, pf_max) -> ([0, 3], [5, 20]), pulse frequency, default = (0, 10)
Examples
~~~~~~~~
>>> import numpy as np
>>> from mealpy.swarm_based.BA import ModifiedBA
>>>
>>> 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
>>> pulse_rate = 0.95
>>> pulse_frequency = [0, 10]
>>> model = ModifiedBA(problem_dict1, epoch, pop_size, pulse_rate, pulse_frequency)
>>> best_position, best_fitness = model.solve()
>>> print(f"Solution: {best_position}, Fitness: {best_fitness}")
"""
def __init__(self, problem, epoch=10000, pop_size=100, pulse_rate=0.95, pulse_frequency=(0, 10), **kwargs):
"""
Args:
problem (dict): The problem dictionary
epoch (int): maximum number of iterations, default = 10000
pop_size (int): number of population size, default = 100
pulse_rate (float): pulse rate / emission rate, default = 0.95
pulse_frequency (list): (pf_min, pf_max): pulse frequency, default = (0, 10)
"""
super().__init__(problem, kwargs)
self.nfe_per_epoch = pop_size
self.sort_flag = False
self.epoch = epoch
self.pop_size = pop_size
self.pulse_rate = pulse_rate
self.pulse_frequency = pulse_frequency
self.alpha = self.gamma = 0.9
self.dyn_list_velocity = np.zeros((self.pop_size, self.problem.n_dims))
[docs] def evolve(self, epoch):
"""
The main operations (equations) of algorithm. Inherit from Optimizer class
Args:
epoch (int): The current iteration
"""
nfe_epoch = 0
pop_new = []
for idx in range(0, self.pop_size):
pf = self.pulse_frequency[0] + (self.pulse_frequency[1] - self.pulse_frequency[0]) * np.random.uniform() # Eq. 2
self.dyn_list_velocity[idx] = np.random.uniform() * self.dyn_list_velocity[idx] + \
(self.g_best[self.ID_POS] - self.pop[idx][self.ID_POS]) * pf # Eq. 3
x = self.pop[idx][self.ID_POS] + self.dyn_list_velocity[idx] # Eq. 4
pos_new = self.amend_position(x)
pop_new.append([pos_new, None])
pop_new = self.update_fitness_population(pop_new)
nfe_epoch += self.pop_size
pop_child_idx = []
pop_child = []
for idx in range(0, self.pop_size):
if self.compare_agent(pop_new[idx], self.pop[idx]):
self.pop[idx] = deepcopy(pop_new[idx])
else:
if np.random.random() > self.pulse_rate:
x = self.g_best[self.ID_POS] + 0.01 * np.random.uniform(self.problem.lb, self.problem.ub)
pos_new = self.amend_position(x)
pop_child_idx.append(idx)
pop_child.append([pos_new, None])
nfe_epoch += 1
pop_child = self.update_fitness_population(pop_child)
for idx, idx_selected in enumerate(pop_child_idx):
if self.compare_agent(pop_child[idx], pop_new[idx_selected]):
pop_new[idx_selected] = deepcopy(pop_child[idx])
self.pop = pop_new
self.nfe_per_epoch = nfe_epoch