Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature smpso #6

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
276 changes: 276 additions & 0 deletions algorithms/SMPSO/SMPSO.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
import copy
import logging
import random

from algorithms.base.driver import Driver
from algorithms.base.drivertools import mutate, crossover


class SMPSO(Driver):
ETA = 0.0075

def __init__(
self,
population,
fitnesses,
dims,
mutation_eta,
mutation_rate,
crossover_eta,
crossover_rate,
mutation_perturbation=0.5,
mutation_probability=0.05,
trim_function=lambda x: x,
fitness_archive=None,
*args,
**kwargs
):
super().__init__(*args, **kwargs)
self.fitnesses = fitnesses
self.dims = dims
self.mutation_eta = mutation_eta
self.mutation_rate = mutation_rate
self.trim_function = trim_function
self.individuals = [Individual(trim_function(x)) for x in population]
self.mutation_probability = mutation_probability

self.leaders_size = len(population) # parameter?
self.mutation_perturbation = mutation_perturbation
self.crowding_selector = CrowdingTournament()


self.archive = Archive(self.ETA)
self.leader_archive = LeaderArchive(self.leaders_size)
self.fitness_archive = fitness_archive

self.init()

@property
def population(self):
return [x.value for x in self.individuals]

@population.setter
def population(self, pop):
self.individuals = [Individual(x) for x in pop]

def init_personal_best(self):
for p in self.individuals:
p.best_val = copy.deepcopy(p)

def init(self):
self.logger = logging.getLogger(__name__)
self.cost = 0
self.gen_no = 0
self.cost += self.calculate_objectives()
self.init_leaders()
self.init_personal_best()
self.leader_archive.crowding()

def init_leaders(self):
for p in self.individuals:
if self.leader_archive.add(copy.deepcopy(p)):
self.archive.add(copy.deepcopy(p))

def finalized_population(self):
return [x.value for x in self.archive]

def step(self):
# print("{}: {} : {}".format(gen_no, len(self.leader_archive.archive), len(self.archive.archive)))

self.compute_speed()
self.move()

# dirty hack for unknown evolution length

#progress = min(1.0, self.cost / self.max_budget) if self.max_budget else None
self.mutation()

for x in self.individuals:
x.value = self.trim_function(x.value)

self.cost += self.calculate_objectives()

self.update_leaders()
self.update_personal_best()

self.leader_archive.crowding()

self.logger.debug(
"{}: {} : {}".format(
self.gen_no, len(self.leader_archive.archive), len(self.archive.archive)
)
)
self.gen_no += 1

def update_personal_best(self):
for p in self.individuals:
# print("new: {}, best: {}".format(p.objectives, p.best_val.objectives))
if p.dominates(p.best_val):
# print("new best: {}".format(p.objectives))
p.best_val = copy.deepcopy(p)

def update_leaders(self):
for p in self.individuals:
new_ind = copy.deepcopy(p)
if self.leader_archive.add(new_ind):
self.archive.add(copy.deepcopy(p))

def calculate_objectives(self):
objectives_cost = 0
for p in self.individuals:
if (self.fitness_archive is not None) and (p.value in self.fitness_archive):
p.objectives = self.fitness_archive[p.value]
objectives_cost = 0
else:
p.objectives = [o(p.value) for o in self.fitnesses]
objectives_cost = len(self.individuals)
return objectives_cost

def move(self):
for p in self.individuals:
for i in range(len(self.dims)):
new_x = p.value[i] + p.speed[i]
bounded_x = max(new_x, self.dims[i][0])
bounded_x = min(bounded_x, self.dims[i][1])

if bounded_x != new_x:
p.speed[i] *= 0.0001

p.value[i] = bounded_x

def compute_speed(self):
for p in self.individuals:
best_global = (
self.crowding_selector(self.leader_archive)
if len(self.leader_archive.archive) > 1
else self.leader_archive.archive[0]
)

r1 = random.uniform(0, 1)
r2 = random.uniform(0, 1)
C1 = random.uniform(1.5, 2.5)
C2 = random.uniform(1.5, 2.5)
W = random.uniform(0.1, 0.5)

for j in range(len(self.dims)):
p.speed[j] = (
W * p.speed[j]
+ C1 * r1 * (p.best_val.value[j] - p.value[j])
+ C2 * r2 * (best_global.value[j] - p.value[j])
)

def mutation(self):
stop_indice = round(( len(self.individuals)*15) / 100) # We mutate on 15% of the population

for cur in self.individuals[0:stop_indice]:
mutate(cur.value, self.dims, self.mutation_rate, self.mutation_eta)


class CrowdingTournament:
def __init__(self, tournament_size=2):
self.tournament_size = tournament_size

def __call__(self, pool):
sub_pool = random.sample(pool.archive, self.tournament_size)
return min(sub_pool, key=lambda x: x.crowd_val)


class Individual:
def __init__(self, value):
self.value = value
self.best_val = None
self.crowd_val = 0
self.objectives = []
self.reset_speed()

def __deepcopy__(self, memo):
dup = Individual(copy.deepcopy(self.value, memo))
dup.best_val = copy.deepcopy(self.best_val, memo)
dup.speed = copy.deepcopy(self.speed, memo)
dup.crowd_val = self.crowd_val
dup.objectives = copy.deepcopy(self.objectives, memo)
return dup

def dominates(self, p2, eta=0):
at_least_one = False
for i in range(len(self.objectives)):
if p2.objectives[i] < self.objectives[i] / (1 + eta):
return False
elif p2.objectives[i] > self.objectives[i] / (1 + eta):
at_least_one = True

return at_least_one

def reset_speed(self):
self.speed = [0] * len(self.value)

def equal_obj(self, p):
for i in range(len(self.objectives)):
if self.objectives[i] != p.objectives[i]:
return False
return True


class Archive(object):
def __init__(self, eta=0.0):
self.archive = []
self.eta = eta

def __iter__(self):
return self.archive.__iter__()

def add(self, p):

for l in self.archive:
if l.dominates(p, self.eta):
return False
elif p.dominates(l, self.eta):
self.archive.remove(l)
elif l.equal_obj(p):
return False

self.archive.append(p)
return True


class LeaderArchive(Archive):
def __init__(self, size):
super().__init__()
self.size = size

def add(self, p):
added = super().add(p)
if added and len(self.archive) > self.size:
self.prune()
return added

def prune(self):
self.crowding()
worst_res = max(self.archive, key=lambda x: x.crowd_val)
self.archive.remove(worst_res)

def crowding(self):
for p in self.archive:
p.crowd_val = 0

obj_n = len(self.archive[0].objectives)

archive_len = len(self.archive)
for i in range(obj_n):
self.archive.sort(key=lambda x: x.objectives[i])
obj_min = self.archive[0].objectives[i]
obj_max = self.archive[archive_len - 1].objectives[i]

self.archive[0].crowd_val = float("inf")
self.archive[archive_len - 1].crowd_val = float("inf")

for j in range(1, archive_len - 1):
if obj_max - obj_min > 0:
dist = (
self.archive[j].objectives[i]
- self.archive[j - 1].objectives[i]
)
dist /= obj_max - obj_min
self.archive[j].crowd_val += dist
else:
self.archive[j].crowd_val = float("inf")
37 changes: 37 additions & 0 deletions algorithms/SMPSO/message.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from algorithms.HGS.distributed.message import HGSMessageAdapter
from algorithms.IMGA.message import IMGAMessageAdapter
from algorithms.base.model import SubPopulation


class SMPSOIMGAMessageAdapter(IMGAMessageAdapter):
def get_population(self):
return [x.value for x in self.driver.individuals]

def immigrate(self, migrants):
for migrant in migrants:
migrant.reset_speed()
self.driver.individuals.append(migrant)

def emigrate(self, migrants: SubPopulation):
immigrants_cp = list(migrants)
to_remove = []

for p in self.driver.individuals:
if p.value in immigrants_cp:
to_remove.append(p)
immigrants_cp.remove(p.value)

for p in to_remove:
self.driver.individuals.remove(p)
return to_remove


class SMPSOHGSMessageAdapter(HGSMessageAdapter):
def get_population(self):
return self.driver.population

def nominate_delegates(self):
return self.get_population()


SMPSODHGSMessageAdapter = SMPSOHGSMessageAdapter
2 changes: 2 additions & 0 deletions algorithms/SMPSO/notes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- Ask a few questions: Understand how to run the platform, run the algorithm, see if it works well

9 changes: 7 additions & 2 deletions plots/pictures.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,15 @@
NSGAIII_LS = [5, 2] # '-- --'
IBEA_LS = [2, 2] # '.....'
OMOPSO_LS = [10, 2, 5, 2] # '-.'
SMPSO_LS = [5, 1, 3, 1] # '.....'
JGBL_LS = [2, 10] # ': : :'
NSLS_LS = [4, 30] # ': : :'

SPEA2_M = "o"
NSGAII_M = "*"
IBEA_M = "^"
OMOPSO_M = ">"
SMPSO_M = "s"
NSGAIII_M = "v"
JGBL_M = "<"
NSLS_M = "x"
Expand All @@ -75,7 +77,7 @@
print(algos)


algos_order = ["NSGAII", "IBEA", "OMOPSO", "NSGAIII", "JGBL", "NSLS"]
algos_order = ["NSGAII", "IBEA", "OMOPSO", "NSGAIII", "JGBL", "NSLS", "SMPSO"]

algos_base = list(algos_order)
for meta in run_config.metaalgorithms:
Expand All @@ -85,12 +87,13 @@
algos_groups_configuration_all_together = {tuple(algos_order): ("",)}

algos_groups_configuration_splitted = {
("SPEA2", "NSGAII", "IBEA", "OMOPSO", "NSGAIII", "SMSEMOA", "JGBL", "NSLS"): (0, 1),
("SPEA2", "NSGAII", "IBEA", "OMOPSO", "SMPSO", "NSGAIII", "SMSEMOA", "JGBL", "NSLS"): (0, 1),
(
"IMGA+SPEA2",
"IMGA+NSGAII",
"IMGA+IBEA",
"IMGA+OMOPSO",
"IMGA+SMPSO",
"IMGA+NSGAIII",
"IMGA+SMSEMOA",
"IMGA+JGBL",
Expand All @@ -101,6 +104,7 @@
"HGS+NSGAII",
"HGS+IBEA",
"HGS+OMOPSO",
"HGS+SMPSO",
"HGS+NSGAIII",
"HGS+SMSEMOA",
"HGS+JGBL",
Expand All @@ -115,6 +119,7 @@
("NSGAIII", "IMGA+NSGAIII", "HGS+NSGAIII"): ("_nsgaiii",),
("SMSEMOA", "IMGA+SMSEMOA", "HGS+SMSEMOA"): ("_smsemoa",),
("OMOPSO", "IMGA+OMOPSO", "HGS+OMOPSO"): ("_omopso",),
("SMPSO", "IMGA+SMPSO", "HGS+SMPSO"): ("_smpso",),
("JGBL", "IMGA+JGBL", "HGS+JGBL"): ("_jgbl",),
("NSLS", "IMGA+NSLS", "HGS+NSLS"): ("_nsls",),
}
Expand Down
4 changes: 3 additions & 1 deletion simulation/run_config.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from itertools import product

drivers = ["SPEA2", "IBEA", "NSGAII", "OMOPSO", "NSGAIII", "JGBL", "SMSEMOA", "NSLS"]
drivers = ["SPEA2", "IBEA", "NSGAII", "OMOPSO", "NSGAIII", "JGBL", "SMSEMOA", "NSLS", "SMPSO"]

metaalgorithms = ["IMGA", "HGS", "DHGS"]

Expand Down Expand Up @@ -119,6 +119,8 @@ def init_alg___JGBL(algo_config, problem_mod):
def init_alg___OMOPSO(algo_config, problem_mod):
standard_variance(algo_config, problem_mod)

def init_alg___SMPSO(algo_config, problem_mod):
standard_variance(algo_config, problem_mod)

def init_alg___SMSEMOA(algo_config, problem_mod):
standard_variance(algo_config, problem_mod)
Expand Down