Algorithm.py 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. """Abstract Algorithm class used as basic algorithm implementation with some specific initialization
  2. """
  3. # main imports
  4. import logging
  5. from ..utils.color import macop_text, macop_line, macop_progress
  6. # Generic algorithm class
  7. class Algorithm():
  8. """Algorithm class used as basic algorithm
  9. Attributes:
  10. initalizer: {function} -- basic function strategy to initialize solution
  11. evaluator: {function} -- basic function in order to obtained fitness (mono or multiple objectives)
  12. operators: {[Operator]} -- list of operator to use when launching algorithm
  13. policy: {Policy} -- Policy class implementation strategy to select operators
  14. validator: {function} -- basic function to check if solution is valid or not under some constraints
  15. maximise: {bool} -- specify kind of optimization problem
  16. currentSolution: {Solution} -- current solution managed for current evaluation
  17. bestSolution: {Solution} -- best solution found so far during running algorithm
  18. checkpoint: {Checkpoint} -- Checkpoint class implementation to keep track of algorithm and restart
  19. parent: {Algorithm} -- parent algorithm reference in case of inner Algorithm instance (optional)
  20. """
  21. def __init__(self,
  22. _initalizer,
  23. _evaluator,
  24. _operators,
  25. _policy,
  26. _validator,
  27. _maximise=True,
  28. _parent=None):
  29. self.initializer = _initalizer
  30. self.evaluator = _evaluator
  31. self.operators = _operators
  32. self.policy = _policy
  33. self.validator = _validator
  34. self.checkpoint = None
  35. self.bestSolution = None
  36. # other parameters
  37. self.parent = _parent # parent algorithm if it's sub algorithm
  38. #self.maxEvaluations = 0 # by default
  39. self.maximise = _maximise
  40. self.initRun()
  41. def addCheckpoint(self, _class, _every, _filepath):
  42. """Add checkpoint to algorithm specifying usefull parameters
  43. Args:
  44. _class: {class} -- Checkpoint class type
  45. _every: {int} -- checkpoint frequency based on evaluations
  46. _filepath: {str} -- file path where checkpoints will be saved
  47. """
  48. self.checkpoint = _class(self, _every, _filepath)
  49. def setCheckpoint(self, _checkpoint):
  50. """Set checkpoint instance directly
  51. Args:
  52. _checkpoint: {Checkpoint} -- checkpoint instance
  53. """
  54. self.checkpoint = _checkpoint
  55. def resume(self):
  56. """Resume algorithm using checkpoint instance
  57. Raises:
  58. ValueError: No checkpoint initialize (use `addCheckpoint` or `setCheckpoint` is you want to use this process)
  59. """
  60. if self.checkpoint is None:
  61. raise ValueError(
  62. "Need to `addCheckpoint` or `setCheckpoint` is you want to use this process"
  63. )
  64. else:
  65. print(macop_line())
  66. print(
  67. macop_text('Checkpoint found from `{}` file.'.format(
  68. self.checkpoint.filepath)))
  69. self.checkpoint.load()
  70. def initRun(self):
  71. """
  72. Method which initialiazes or re-initializes the whole algorithm context: operators, current solution, best solution (by default current solution)
  73. """
  74. # track reference of algo into operator (keep an eye into best solution)
  75. for operator in self.operators:
  76. operator.setAlgo(self)
  77. # also track reference for policy
  78. self.policy.setAlgo(self)
  79. self.currentSolution = self.initializer()
  80. # evaluate current solution
  81. self.currentSolution.evaluate(self.evaluator)
  82. # reinitialize policy
  83. # if self.parent is not None:
  84. # self.policy = globals()[type(self.policy).__name__]()
  85. # keep in memory best known solution (current solution)
  86. self.bestSolution = self.currentSolution
  87. def increaseEvaluation(self):
  88. """
  89. Increase number of evaluation once a solution is evaluated
  90. """
  91. self.numberOfEvaluations += 1
  92. if self.parent is not None:
  93. self.parent.numberOfEvaluations += 1
  94. def getGlobalEvaluation(self):
  95. """Get the global number of evaluation (if inner algorithm)
  96. Returns:
  97. {int} -- current global number of evaluation
  98. """
  99. if self.parent is not None:
  100. return self.parent.numberOfEvaluations
  101. return self.numberOfEvaluations
  102. def getGlobalMaxEvaluation(self):
  103. """Get the global max number of evaluation (if inner algorithm)
  104. Returns:
  105. {int} -- current global max number of evaluation
  106. """
  107. if self.parent is not None:
  108. return self.parent.maxEvaluations
  109. return self.maxEvaluations
  110. def stop(self):
  111. """
  112. Global stopping criteria (check for inner algorithm too)
  113. """
  114. if self.parent is not None:
  115. return self.parent.numberOfEvaluations >= self.parent.maxEvaluations or self.numberOfEvaluations >= self.maxEvaluations
  116. return self.numberOfEvaluations >= self.maxEvaluations
  117. def evaluate(self, _solution):
  118. """
  119. Evaluate a solution using evaluator passed when intialize algorithm
  120. Args:
  121. solution: {Solution} -- solution to evaluate
  122. Returns:
  123. fitness score of solution which is not already evaluated or changed
  124. Note:
  125. if multi-objective problem this method can be updated using array of `evaluator`
  126. """
  127. return _solution.evaluate(self.evaluator)
  128. def update(self, _solution):
  129. """
  130. Apply update function to solution using specific `policy`
  131. Check if solution is valid after modification and returns it
  132. Args:
  133. solution: {Solution} -- solution to update using current policy
  134. Returns:
  135. {Solution} -- updated solution obtained by the selected operator
  136. """
  137. # two parameters are sent if specific crossover solution are wished
  138. sol = self.policy.apply(_solution)
  139. if (sol.isValid(self.validator)):
  140. return sol
  141. else:
  142. logging.info("-- New solution is not valid %s" % sol)
  143. return _solution
  144. def isBetter(self, _solution):
  145. """
  146. Check if solution is better than best found
  147. Args:
  148. solution: {Solution} -- solution to compare with best one
  149. Returns:
  150. {bool} -- `True` if better
  151. """
  152. # depending of problem to solve (maximizing or minimizing)
  153. if self.maximise:
  154. if _solution.fitness() > self.bestSolution.fitness():
  155. return True
  156. else:
  157. if _solution.fitness() < self.bestSolution.fitness():
  158. return True
  159. # by default
  160. return False
  161. def run(self, _evaluations):
  162. """
  163. Run the specific algorithm following number of evaluations to find optima
  164. """
  165. self.maxEvaluations = _evaluations
  166. self.initRun()
  167. # check if global evaluation is used or not
  168. if self.parent is not None and self.getGlobalEvaluation() != 0:
  169. # init number evaluations of inner algorithm depending of globalEvaluation
  170. # allows to restart from `checkpoint` last evaluation into inner algorithm
  171. rest = self.getGlobalEvaluation() % self.maxEvaluations
  172. self.numberOfEvaluations = rest
  173. else:
  174. self.numberOfEvaluations = 0
  175. logging.info("Run %s with %s evaluations" %
  176. (self.__str__(), _evaluations))
  177. def progress(self):
  178. """
  179. Log progress and apply checkpoint if necessary
  180. """
  181. if self.checkpoint is not None:
  182. self.checkpoint.run()
  183. macop_progress(self.getGlobalEvaluation(),
  184. self.getGlobalMaxEvaluation())
  185. logging.info("-- %s evaluation %s of %s (%s%%) - BEST SCORE %s" %
  186. (type(self).__name__, self.numberOfEvaluations,
  187. self.maxEvaluations, "{0:.2f}".format(
  188. (self.numberOfEvaluations) / self.maxEvaluations *
  189. 100.), self.bestSolution.fitness()))
  190. def end(self):
  191. """Display end message into `run` method
  192. """
  193. print(
  194. macop_text('({}) Found after {} evaluations => {}'.format(
  195. type(self).__name__, self.numberOfEvaluations,
  196. self.bestSolution)))
  197. print(macop_line())
  198. def information(self):
  199. logging.info("-- Best %s - SCORE %s" %
  200. (self.bestSolution, self.bestSolution.fitness()))
  201. def __str__(self):
  202. return "%s using %s" % (type(self).__name__, type(
  203. self.bestSolution).__name__)