MOEAD.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. """Multi-Ojective Evolutionary Algorithm with Scalar Decomposition algorithm
  2. """
  3. # main imports
  4. import logging
  5. import math
  6. import numpy as np
  7. # module imports
  8. from ..Algorithm import Algorithm
  9. from .MOSubProblem import MOSubProblem
  10. def moEvaluator(_solution, _evaluator, _weights):
  11. scores = [ eval(_solution) for eval in _evaluator ]
  12. # associate objectives scores to solution
  13. _solution.scores = scores
  14. return sum([scores[i] for i, w in enumerate(_weights) ])
  15. class MOEAD(Algorithm):
  16. """Multi-Ojective Evolutionary Algorithm with Scalar Decomposition
  17. Attributes:
  18. mu: {int} -- number of sub problems
  19. T: {[float]} -- number of neightbors for each sub problem
  20. initalizer: {function} -- basic function strategy to initialize solution
  21. evaluator: {[function]} -- list of basic function in order to obtained fitness (multiple objectives)
  22. operators: {[Operator]} -- list of operator to use when launching algorithm
  23. policy: {Policy} -- Policy class implementation strategy to select operators
  24. validator: {function} -- basic function to check if solution is valid or not under some constraints
  25. maximise: {bool} -- specify kind of optimization problem
  26. currentSolution: {Solution} -- current solution managed for current evaluation
  27. bestSolution: {Solution} -- best solution found so far during running algorithm
  28. callbacks: {[Callback]} -- list of Callback class implementation to do some instructions every number of evaluations and `load` when initializing algorithm
  29. """
  30. def __init__(self,
  31. _mu,
  32. _T,
  33. _initalizer,
  34. _evaluator,
  35. _operators,
  36. _policy,
  37. _validator,
  38. _maximise=True,
  39. _parent=None):
  40. # redefinition of constructor to well use `initRun` method
  41. self.initializer = _initalizer
  42. self.evaluator = _evaluator
  43. self.operators = _operators
  44. self.policy = _policy
  45. self.validator = _validator
  46. self.callbacks = []
  47. # by default
  48. self.numberOfEvaluations = 0
  49. self.maxEvaluations = 0
  50. # other parameters
  51. self.parent = _parent # parent algorithm if it's sub algorithm
  52. #self.maxEvaluations = 0 # by default
  53. self.maximise = _maximise
  54. # track reference of algo into operator (keep an eye into best solution)
  55. for operator in self.operators:
  56. operator.setAlgo(self)
  57. # also track reference for policy
  58. self.policy.setAlgo(self)
  59. if _mu < _T:
  60. raise ValueError('`mu` cannot be less than `T`')
  61. self.mu = _mu
  62. self.T = _T
  63. # initialize neighbors for each sub problem
  64. self.setNeighbors()
  65. weights = []
  66. for i in range(self.mu):
  67. angle = math.pi / 2 * i / (self.mu - 1)
  68. weights.append([math.cos(angle), math.sin(angle)])
  69. self.subProblems = []
  70. for i in range(self.mu):
  71. # compute weight sum from solution
  72. sub_evaluator = lambda _solution: moEvaluator(_solution, _evaluator, weights[i])
  73. # intialize each sub problem
  74. subProblem = MOSubProblem(i, weights[i], _initalizer,
  75. sub_evaluator, _operators, _policy,
  76. _validator, _maximise, self)
  77. self.subProblems.append(subProblem)
  78. self.population = [None for n in range(self.mu)]
  79. self.refPoint = (0., 0.)
  80. def initRun(self):
  81. """
  82. Method which initialiazes or re-initializes the whole algorithm context specifically for MOEAD
  83. """
  84. # initialization is done during run method
  85. pass
  86. def run(self, _evaluations):
  87. """
  88. Run the local search algorithm
  89. Args:
  90. _evaluations: {int} -- number of Local search evaluations
  91. Returns:
  92. {Solution} -- best solution found
  93. """
  94. # by default use of mother method to initialize variables
  95. super().run(_evaluations)
  96. # enable callback resume for MOEAD
  97. self.resume()
  98. # initialize each sub problem
  99. for i in range(self.mu):
  100. self.subProblems[i].run(1)
  101. self.population[i] = self.subProblems[i].bestSolution
  102. # MOEAD algorithm implementation
  103. while not self.stop():
  104. for i in range(self.mu):
  105. # run 1 iteration into sub problem `i`
  106. self.subProblems[i].run(1)
  107. spBestSolution = self.subProblems[i].bestSolution
  108. self.updateMinRefPoint(spBestSolution)
  109. # stop algorithm if necessary
  110. if self.stop():
  111. break
  112. logging.info("End of %s, best solution found %s" %
  113. (type(self).__name__, self.population))
  114. self.end()
  115. return self.bestSolution
  116. def setNeighbors(self):
  117. dmin = dmax = 0
  118. if self.T % 2 == 1:
  119. dmin = -int(self.T / 2)
  120. dmax = int(self.T / 2) + 1
  121. else:
  122. dmin = -int(self.T / 2) + 1
  123. dmax = +self.T / 2
  124. # init neighbord list
  125. self.neighbors = [[] for n in range(self.mu)]
  126. for direction in range(0, -dmin):
  127. for i in range(self.T):
  128. self.neighbors[direction].append(i)
  129. for direction in range(-dmin, self.mu - dmax):
  130. for i in range(direction + dmin, direction + dmax):
  131. self.neighbors[direction].append(i)
  132. for direction in range(self.mu - dmax, self.mu):
  133. for i in range(self.mu - self.T, self.mu):
  134. self.neighbors[direction].append(i)
  135. def updateMinRefPoint(self, _solution):
  136. pass
  137. def nonDominated():
  138. pass
  139. def __str__(self):
  140. return "%s using %s" % (type(self).__name__, type(
  141. self.population).__name__)