# !/usr/bin/env python
# Created by "Thieu" at 14:51, 17/03/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 BaseSRSR(Optimizer):
"""
The original version of: Swarm Robotics Search And Rescue (SRSR)
Links:
1. https://doi.org/10.1016/j.asoc.2017.02.028
Examples
~~~~~~~~
>>> import numpy as np
>>> from mealpy.swarm_based.SRSR import BaseSRSR
>>>
>>> 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
>>> model = BaseSRSR(problem_dict1, epoch, pop_size)
>>> best_position, best_fitness = model.solve()
>>> print(f"Solution: {best_position}, Fitness: {best_fitness}")
References
~~~~~~~~~~
[1] Bakhshipour, M., Ghadi, M.J. and Namdari, F., 2017. Swarm robotics search & rescue: A novel
artificial intelligence-inspired optimization approach. Applied Soft Computing, 57, pp.708-726.
"""
ID_POS = 0
ID_TAR = 1
ID_MU = 2
ID_SIGMA = 3
ID_POS_NEW = 4
ID_FIT_NEW = 5
ID_FIT_MOVE = 6
def __init__(self, problem, epoch=10000, pop_size=100, **kwargs):
"""
Args:
problem (dict): The problem dictionary
epoch (int): maximum number of iterations, default = 10000
pop_size (int): number of population size, default = 100
"""
super().__init__(problem, kwargs)
self.nfe_per_epoch = pop_size
self.sort_flag = True
self.epoch = epoch
self.pop_size = pop_size
[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, ...]], mu, sigma, x_new, fit_new, fit_move]
"""
position = np.random.uniform(self.problem.lb, self.problem.ub)
position = self.amend_position(position)
fitness = self.get_fitness_position(position=position)
mu = 0
sigma = 0
x_new = deepcopy(position)
fit_new = deepcopy(fitness)
fit_move = 0
return [position, fitness, mu, sigma, x_new, fit_new, fit_move]
[docs] def initialization(self):
self.pop = self.create_population(self.pop_size)
self.pop, self.g_best = self.get_global_best_solution(self.pop)
# Control Parameters Of Algorithm
# ==============================================================================================
# [c1] movement_factor : Determines Movement Pace Of Robots During Exploration Policy
# [c2] sigma_factor : Determines Level Of Divergence Of Slave Robots From Master Robot
# [c3] sigma_limit : Limits Value Of Sigma Factor
# [c4] mu_factor : Controls Mean Value For Master And Slave Robots
# Control Parameters C1, C2 And C3 Are Automatically Tuned While C4 Should Be Set By User
# ==============================================================================================
self.mu_factor = 2 / 3 # [0.1-0.9] Controls Dominance Of Master Robot, Preferably 2/3
self.sigma_temp = np.zeros(self.pop_size) # Initializing Temporary Stacks
self.SIF = None
self.movement_factor = self.problem.ub - self.problem.lb
[docs] def evolve(self, epoch):
"""
The main operations (equations) of algorithm. Inherit from Optimizer class
Args:
epoch (int): The current iteration
"""
# ========================================================================================= %%
# PHASE 1 (ACCUMULATION): CALCULATING Mu AND SIGMA values FOR SOLUTIONS %
# ===========================================================================================%%
nfe_epoch = 0
# ------ CALCULATING MU AND SIGMA FOR MASTER ROBOT ----------
self.pop[0][self.ID_SIGMA] = np.random.uniform()
if epoch % 2 == 1:
self.pop[0][self.ID_MU] = (1 - self.pop[0][self.ID_SIGMA]) * self.pop[0][self.ID_POS]
else:
self.pop[0][self.ID_MU] = (1 + (1 - self.mu_factor) * self.pop[0][self.ID_SIGMA]) * self.pop[0][self.ID_POS]
pop_new = []
for i in range(0, self.pop_size):
agent = deepcopy(self.pop[i])
# ---------- CALCULATING MU AND SIGMA FOR SLAVE ROBOTS ---------
self.pop[i][self.ID_MU] = self.mu_factor * self.pop[0][self.ID_POS] + (1 - self.mu_factor) * self.pop[i][self.ID_POS]
if epoch == 0:
self.SIF = 6
self.sigma_temp[i] = self.SIF * np.random.uniform()
self.pop[i][self.ID_SIGMA] = self.sigma_temp[i] * abs(self.pop[0][self.ID_POS] - self.pop[i][self.ID_POS]) + \
np.random.uniform() ** 2 * ((self.pop[0][self.ID_POS] - self.pop[i][self.ID_POS]) < 0.05)
# ----- Generating New Positions Using New Obtained Mu And Sigma Values --------------
pos_new = np.random.normal(self.pop[i][self.ID_MU], self.pop[i][self.ID_SIGMA], self.problem.n_dims)
agent[self.ID_POS] = self.amend_position(pos_new)
pop_new.append(agent)
pop_new = self.update_fitness_population(pop_new)
nfe_epoch += self.pop_size
for idx in range(0, self.pop_size):
# --------- Calculate Degree Of Cost Movement Of Robots During Movement --------------
self.pop[idx][self.ID_FIT_MOVE] = self.pop[idx][self.ID_TAR][self.ID_FIT] - self.pop[idx][self.ID_FIT_NEW][self.ID_FIT]
self.pop[idx][self.ID_POS_NEW] = deepcopy(pop_new[idx][self.ID_POS])
self.pop[idx][self.ID_FIT_NEW] = deepcopy(pop_new[idx][self.ID_TAR])
# ---------- Progress Assessment: Replacing More Quality Solutions With Previous Ones ------
# Replace Solution If It Reached To A More Quality Position
if self.compare_agent(pop_new[idx], self.pop[idx]):
self.pop[idx][self.ID_POS] = deepcopy(pop_new[idx][self.ID_POS])
self.pop[idx][self.ID_TAR] = deepcopy(pop_new[idx][self.ID_TAR])
# --------- Determining Sigma Improvement Factor (Sif) Based On Vvss Movement -------------------
## Get best improved fitness
fit_id = np.argmax([item[self.ID_FIT_MOVE] for item in self.pop])
sigma_factor = 1 + np.random.uniform() * np.max(self.problem.ub - self.problem.lb)
self.SIF = sigma_factor * self.sigma_temp[fit_id]
# Controlling Parameter Of Algorithm
if self.SIF > np.max(self.problem.ub):
self.SIF = np.max(self.problem.ub) * np.random.uniform()
# ========================================================================================= %%
# Phase 2 (Exploration): Moving Slave Robots Toward Master Robot %
# ===========================================================================================%%
pop_new = []
for i in range(0, self.pop_size):
agent = deepcopy(self.pop[i])
gb = np.random.uniform(-1, 1, self.problem.n_dims)
gb[gb >= 0] = 1
gb[gb < 0] = -1
pos_new = self.pop[i][self.ID_POS] * np.random.uniform() + gb * (self.pop[0][self.ID_POS] - self.pop[i][self.ID_POS]) + \
self.movement_factor * np.random.uniform(self.problem.lb, self.problem.ub)
agent[self.ID_POS] = self.amend_position(pos_new)
pop_new.append(agent)
pop_new = self.update_fitness_population(pop_new)
nfe_epoch += self.pop_size
for idx in range(0, self.pop_size):
# --------- Calculate Degree Of Cost Movement Of Robots During Movement --------------
self.pop[idx][self.ID_FIT_MOVE] = self.pop[idx][self.ID_TAR][self.ID_FIT] - self.pop[idx][self.ID_FIT_NEW][self.ID_FIT]
self.pop[idx][self.ID_POS_NEW] = deepcopy(pop_new[idx][self.ID_POS])
self.pop[idx][self.ID_FIT_NEW] = deepcopy(pop_new[idx][self.ID_TAR])
# ---------- Progress Assessment: Replacing More Quality Solutions With Previous Ones ------
# Replace Solution If It Reached To A More Quality Position
if self.compare_agent(pop_new[idx], self.pop[idx]):
self.pop[idx][self.ID_POS] = deepcopy(pop_new[idx][self.ID_POS])
self.pop[idx][self.ID_TAR] = deepcopy(pop_new[idx][self.ID_TAR])
# ========================================================================================= %%
# PHASE 3 (LOCAL SEARCH): CREATING SOME WORKER ROBOTS ASSIGNED TO SEARCH %
# LOCATIONS AROUND POSITION OF MASTER ROBOT %
# ===========================================================================================%%
if epoch > 0:
# --- EXTRACTING "INTEGER PART" AND "FRACTIONAL PART" OF THE ELEMENTS OF MASTER RPBOT POSITION------
master_robot = {"original": deepcopy(np.reshape(self.pop[0][self.ID_POS], (self.problem.n_dims, 1))),
"sign": deepcopy(np.reshape(np.sign(self.pop[0][self.ID_POS]), (self.problem.n_dims, 1))),
"abs": deepcopy(np.reshape(abs(self.pop[0][self.ID_POS]), (self.problem.n_dims, 1))),
"int": deepcopy(np.reshape(np.floor(abs(self.pop[0][self.ID_POS])), (self.problem.n_dims, 1))), # INTEGER PART
"frac": deepcopy(np.reshape(abs(self.pop[0][self.ID_POS]) - np.floor(abs(self.pop[0][self.ID_POS])), (self.problem.n_dims, 1)))
} # FRACTIONAL PART
# ------- Applying Nth-root And Nth-exponent Operators To Create Position Of New Worker Robots -------
worker_robot1 = (master_robot["int"] + np.power(master_robot["frac"], 1 / (1 + np.random.randint(1, 4)))) * master_robot["sign"]
id_changed1 = np.argwhere(np.round(np.random.uniform(self.problem.lb, self.problem.ub)))
id_changed1 = np.reshape(id_changed1, (len(id_changed1)))
worker_robot1 = np.reshape(worker_robot1, (self.problem.n_dims, 1))
worker_robot1[id_changed1] = master_robot["original"][id_changed1]
worker_robot2 = (master_robot["int"] + np.power(master_robot["frac"], (1 + np.random.randint(1, 4)))) * master_robot["sign"]
id_changed2 = np.argwhere(np.round(np.random.uniform(self.problem.lb, self.problem.ub)))
id_changed2 = np.reshape(id_changed2, (len(id_changed2)))
worker_robot2 = np.reshape(worker_robot2, (self.problem.n_dims, 1))
worker_robot2[id_changed2] = master_robot["original"][id_changed2]
# -------- Applying A Combined Ga-like Operator To Create Position Of New Worker Robot -------------
random_per_mutation = np.random.permutation(self.problem.n_dims)
sec1 = random_per_mutation[0: int(self.problem.n_dims / 2)]
sec2 = random_per_mutation[int(self.problem.n_dims / 2):]
worker_robot3 = np.zeros((self.problem.n_dims, 1))
worker_robot3[sec1] = (master_robot["int"][sec1] + np.power(master_robot["frac"][sec1],
1 / (1 + np.random.randint(1, 4)))) * master_robot["sign"][sec1]
worker_robot3[sec2] = (master_robot["int"][sec2] + master_robot["frac"][sec2] **
(1 + np.random.randint(1, 4))) * master_robot["sign"][sec2]
id_changed3 = np.argwhere(np.round(np.random.uniform(self.problem.lb, self.problem.ub)))
id_changed3 = np.reshape(id_changed3, (len(id_changed3)))
worker_robot3[id_changed3] = master_robot["original"][id_changed3]
# ------- Applying Round Operators To Create Position Of New Worker Robot -------------------
worker_robot4 = np.ceil(master_robot["abs"]) * master_robot["sign"]
id_changed4 = np.argwhere(np.round(np.random.uniform(self.problem.lb, self.problem.ub)))
id_changed4 = np.reshape(id_changed4, (len(id_changed4)))
worker_robot4[id_changed4] = master_robot["original"][id_changed4]
worker_robot5 = np.floor(master_robot["abs"]) * master_robot["sign"]
id_changed5 = np.argwhere(np.round(np.random.uniform(self.problem.lb, self.problem.ub)))
id_changed5 = np.reshape(id_changed5, (len(id_changed5)))
worker_robot5[id_changed5] = master_robot["original"][id_changed5]
# --------- Progress Assessment: Replacing More Quality Solutions With Previous Ones ---------------
workers = np.concatenate((worker_robot1.T, worker_robot2.T, worker_robot3.T, worker_robot4.T, worker_robot5.T), axis=0)
pop_workers = []
for i in range(0, 5):
pos_new = self.amend_position(workers[i])
pop_workers.append([pos_new, None])
pop_workers = self.update_fitness_population(pop_workers)
nfe_epoch += 5
for i in range(0, 5):
if self.compare_agent(pop_workers[i], self.pop[1]):
self.pop[-(i + 1)][self.ID_POS] = deepcopy(pop_workers[i][self.ID_POS])
self.pop[-(i + 1)][self.ID_TAR] = deepcopy(pop_workers[i][self.ID_TAR])
self.nfe_per_epoch = nfe_epoch