|
@@ -2,14 +2,24 @@
|
|
|
"""
|
|
|
|
|
|
# main imports
|
|
|
+import pkgutil
|
|
|
import logging
|
|
|
import math
|
|
|
import numpy as np
|
|
|
+import sys
|
|
|
+from ...utils.color import macop_text, macop_line, macop_progress
|
|
|
|
|
|
# module imports
|
|
|
from ..Algorithm import Algorithm
|
|
|
from .MOSubProblem import MOSubProblem
|
|
|
|
|
|
+# import all available solutions
|
|
|
+for loader, module_name, is_pkg in pkgutil.walk_packages(
|
|
|
+ path=['macop/solutions'], prefix='macop.solutions.'):
|
|
|
+ _module = loader.find_module(module_name).load_module(module_name)
|
|
|
+ globals()[module_name] = _module
|
|
|
+
|
|
|
+
|
|
|
def moEvaluator(_solution, _evaluator, _weights):
|
|
|
|
|
|
scores = [ eval(_solution) for eval in _evaluator ]
|
|
@@ -33,7 +43,8 @@ class MOEAD(Algorithm):
|
|
|
validator: {function} -- basic function to check if solution is valid or not under some constraints
|
|
|
maximise: {bool} -- specify kind of optimization problem
|
|
|
currentSolution: {Solution} -- current solution managed for current evaluation
|
|
|
- bestSolution: {Solution} -- best solution found so far during running algorithm
|
|
|
+ population: [{Solution}] -- population of solution, one for each sub problem
|
|
|
+ pfPop: [{Solution}] -- pareto front population
|
|
|
callbacks: {[Callback]} -- list of Callback class implementation to do some instructions every number of evaluations and `load` when initializing algorithm
|
|
|
"""
|
|
|
def __init__(self,
|
|
@@ -101,7 +112,12 @@ class MOEAD(Algorithm):
|
|
|
self.subProblems.append(subProblem)
|
|
|
|
|
|
self.population = [None for n in range(self.mu)]
|
|
|
- self.refPoint = (0., 0.)
|
|
|
+ self.pfPop = []
|
|
|
+
|
|
|
+ if self.maximise:
|
|
|
+ self.refPoint = [ 0 for _ in range(len(_evaluator)) ]
|
|
|
+ else:
|
|
|
+ self.refPoint = [ sys.float_info.max for _ in range(len(_evaluator)) ]
|
|
|
|
|
|
|
|
|
def initRun(self):
|
|
@@ -125,14 +141,15 @@ class MOEAD(Algorithm):
|
|
|
# by default use of mother method to initialize variables
|
|
|
super().run(_evaluations)
|
|
|
|
|
|
- # enable callback resume for MOEAD
|
|
|
- self.resume()
|
|
|
-
|
|
|
# initialize each sub problem
|
|
|
for i in range(self.mu):
|
|
|
self.subProblems[i].run(1)
|
|
|
|
|
|
self.population[i] = self.subProblems[i].bestSolution
|
|
|
+ self.pfPop.append(self.subProblems[i].bestSolution)
|
|
|
+
|
|
|
+ # enable callback resume for MOEAD
|
|
|
+ self.resume()
|
|
|
|
|
|
# MOEAD algorithm implementation
|
|
|
while not self.stop():
|
|
@@ -143,9 +160,30 @@ class MOEAD(Algorithm):
|
|
|
self.subProblems[i].run(1)
|
|
|
spBestSolution = self.subProblems[i].bestSolution
|
|
|
|
|
|
- self.updateMinRefPoint(spBestSolution)
|
|
|
+ self.updateRefPoint(spBestSolution)
|
|
|
+
|
|
|
+ # for each neighbor of current sub problem update solution if better
|
|
|
+ improvment = False
|
|
|
+ for j in self.neighbors[i]:
|
|
|
+ if spBestSolution.fitness() > self.subProblems[j].bestSolution.fitness():
|
|
|
+
|
|
|
+ # create new solution based on current new if better, computes fitness associated to new solution for sub problem
|
|
|
+ class_name = type(spBestSolution).__name__
|
|
|
+ newSolution = getattr(globals()['macop.solutions.' + class_name], class_name)(spBestSolution.data, len(spBestSolution.data))
|
|
|
+ self.subProblems[j].evaluate(newSolution)
|
|
|
+ self.subProblems[j].bestSolution = newSolution
|
|
|
+
|
|
|
+ improvment = True
|
|
|
+
|
|
|
+ # add new solution if improvment is idenfity
|
|
|
+ if improvment:
|
|
|
+ self.pfPop.append(spBestSolution)
|
|
|
|
|
|
+ # update pareto front
|
|
|
+ self.pfPop = self.paretoFront(self.pfPop)
|
|
|
|
|
|
+ # add progress here
|
|
|
+ self.progress()
|
|
|
|
|
|
# stop algorithm if necessary
|
|
|
if self.stop():
|
|
@@ -156,7 +194,24 @@ class MOEAD(Algorithm):
|
|
|
|
|
|
self.end()
|
|
|
|
|
|
- return self.bestSolution
|
|
|
+ return self.pfPop
|
|
|
+
|
|
|
+ def progress(self):
|
|
|
+ """
|
|
|
+ Log progress and apply callbacks if necessary
|
|
|
+ """
|
|
|
+ if len(self.callbacks) > 0:
|
|
|
+ for callback in self.callbacks:
|
|
|
+ callback.run()
|
|
|
+
|
|
|
+ macop_progress(self.getGlobalEvaluation(),
|
|
|
+ self.getGlobalMaxEvaluation())
|
|
|
+
|
|
|
+ logging.info("-- %s evaluation %s of %s (%s%%)" %
|
|
|
+ (type(self).__name__, self.numberOfEvaluations,
|
|
|
+ self.maxEvaluations, "{0:.2f}".format(
|
|
|
+ (self.numberOfEvaluations) / self.maxEvaluations *
|
|
|
+ 100.)))
|
|
|
|
|
|
def setNeighbors(self):
|
|
|
|
|
@@ -185,11 +240,72 @@ class MOEAD(Algorithm):
|
|
|
self.neighbors[direction].append(i)
|
|
|
|
|
|
|
|
|
- def updateMinRefPoint(self, _solution):
|
|
|
- pass
|
|
|
+ def updateRefPoint(self, _solution):
|
|
|
|
|
|
- def nonDominated():
|
|
|
- pass
|
|
|
+ if self.maximise:
|
|
|
+ for i in range(len(self.evaluator)):
|
|
|
+ if _solution.scores[i] > self.refPoint[i]:
|
|
|
+ self.refPoint[i] = _solution.scores[i]
|
|
|
+ else:
|
|
|
+ for i in range(len(self.evaluator)):
|
|
|
+ if _solution.scores[i] < self.refPoint[i]:
|
|
|
+ self.refPoint[i] = _solution.scores[i]
|
|
|
+
|
|
|
+ def paretoFront(self, _population):
|
|
|
+
|
|
|
+ paFront = []
|
|
|
+ indexes = []
|
|
|
+ nObjectives = len(self.evaluator)
|
|
|
+ nSolutions = len(_population)
|
|
|
+
|
|
|
+ # find dominated solution
|
|
|
+ for i in range(nSolutions):
|
|
|
+ for j in range(nSolutions):
|
|
|
+
|
|
|
+ if j in indexes:
|
|
|
+ continue
|
|
|
+
|
|
|
+ nDominated = 0
|
|
|
+
|
|
|
+ # check number of dominated objectives of current solution by the others solution
|
|
|
+ for k in range(len(self.evaluator)):
|
|
|
+ if self.maximise:
|
|
|
+ if _population[i].scores[k] < _population[j].scores[k]:
|
|
|
+ nDominated += 1
|
|
|
+ else:
|
|
|
+ if _population[i].scores[k] > _population[j].scores[k]:
|
|
|
+ nDominated += 1
|
|
|
+
|
|
|
+ if nDominated == nObjectives:
|
|
|
+ indexes.append(i)
|
|
|
+ break
|
|
|
+
|
|
|
+ # store the non dominated solution into pareto front
|
|
|
+ for i in range(nSolutions):
|
|
|
+ if i not in indexes:
|
|
|
+ paFront.append(_population[i])
|
|
|
+
|
|
|
+ return paFront
|
|
|
+
|
|
|
+
|
|
|
+ def end(self):
|
|
|
+ """Display end message into `run` method
|
|
|
+ """
|
|
|
+
|
|
|
+ print(macop_text('({}) Found after {} evaluations'.format(type(self).__name__, self.numberOfEvaluations)))
|
|
|
+
|
|
|
+ for i, solution in enumerate(self.pfPop):
|
|
|
+ print(' - [{}] {} : {}'.format(i, solution.scores, solution))
|
|
|
+
|
|
|
+ print(macop_line())
|
|
|
+
|
|
|
+ def information(self):
|
|
|
+
|
|
|
+ logging.info("-- Pareto front :")
|
|
|
+
|
|
|
+ for i, solution in enumerate(self.pfPop):
|
|
|
+ logging.info("-- %s] SCORE %s - %s" %
|
|
|
+ (i, solution.scores, solution))
|
|
|
|
|
|
def __str__(self):
|
|
|
return "%s using %s" % (type(self).__name__, type(
|