mono.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. """Mono-objective available algorithms
  2. """
  3. # main imports
  4. import logging
  5. # module imports
  6. from macop.algorithms.base import Algorithm
  7. class HillClimberFirstImprovment(Algorithm):
  8. """Hill Climber First Improvment used as quick exploration optimisation algorithm
  9. - First, this algorithm do a neighborhood exploration of a new generated solution (by doing operation on the current solution obtained) in order to find a better solution from the neighborhood space;
  10. - Then replace the current solution by the first one from the neighbordhood space which is better than the current solution;
  11. - And do these steps until a number of evaluation (stopping criterion) is reached.
  12. Attributes:
  13. initalizer: {function} -- basic function strategy to initialise solution
  14. evaluator: {Evaluator} -- evaluator instance in order to obtained fitness (mono or multiple objectives)
  15. operators: {[Operator]} -- list of operator to use when launching algorithm
  16. policy: {Policy} -- Policy class implementation strategy to select operators
  17. validator: {function} -- basic function to check if solution is valid or not under some constraints
  18. maximise: {bool} -- specify kind of optimisation problem
  19. currentSolution: {Solution} -- current solution managed for current evaluation
  20. bestSolution: {Solution} -- best solution found so far during running algorithm
  21. callbacks: {[Callback]} -- list of Callback class implementation to do some instructions every number of evaluations and `load` when initialising algorithm
  22. parent: {Algorithm} -- parent algorithm reference in case of inner Algorithm instance (optional)
  23. Example:
  24. >>> import random
  25. >>> # operators import
  26. >>> from macop.operators.discrete.crossovers import SimpleCrossover
  27. >>> from macop.operators.discrete.mutators import SimpleMutation
  28. >>> # policy import
  29. >>> from macop.policies.classicals import RandomPolicy
  30. >>> # solution and algorithm
  31. >>> from macop.solutions.discrete import BinarySolution
  32. >>> from macop.algorithms.mono import HillClimberFirstImprovment
  33. >>> # evaluator import
  34. >>> from macop.evaluators.discrete.mono import KnapsackEvaluator
  35. >>> # evaluator initialization (worths objects passed into data)
  36. >>> problem_size = 20
  37. >>> worths = [ random.randint(0, 20) for i in range(problem_size) ]
  38. >>> evaluator = KnapsackEvaluator(data={'worths': worths})
  39. >>> # validator specification (based on weights of each objects)
  40. >>> weights = [ random.randint(5, 30) for i in range(problem_size) ]
  41. >>> validator = lambda solution: True if sum([weights[i] for i, value in enumerate(solution.getData()) if value == 1]) < 200 else False
  42. >>> # initialiser function with lambda function
  43. >>> initialiser = lambda x=20: BinarySolution.random(x, validator)
  44. >>> # operators list with crossover and mutation
  45. >>> operators = [SimpleCrossover(), SimpleMutation()]
  46. >>> policy = RandomPolicy(operators)
  47. >>> algo = HillClimberFirstImprovment(initialiser, evaluator, operators, policy, validator, maximise=True, verbose=False)
  48. >>> # run the algorithm
  49. >>> solution = algo.run(100)
  50. >>> solution._score
  51. 128
  52. """
  53. def run(self, evaluations):
  54. """
  55. Run the local search algorithm
  56. Args:
  57. evaluations: {int} -- number of Local search evaluations
  58. Returns:
  59. {Solution} -- best solution found
  60. """
  61. # by default use of mother method to initialise variables
  62. super().run(evaluations)
  63. # initialise current solution and best solution
  64. self.initRun()
  65. solutionSize = self._currentSolution._size
  66. # local search algorithm implementation
  67. while not self.stop():
  68. for _ in range(solutionSize):
  69. # update current solution using policy
  70. newSolution = self.update(self._currentSolution)
  71. # if better solution than currently, replace it and stop current exploration (first improvment)
  72. if self.isBetter(newSolution):
  73. self._bestSolution = newSolution
  74. break
  75. # increase number of evaluations
  76. self.increaseEvaluation()
  77. self.progress()
  78. logging.info(
  79. f"---- Current {newSolution} - SCORE {newSolution.fitness()}"
  80. )
  81. # stop algorithm if necessary
  82. if self.stop():
  83. break
  84. # set new current solution using best solution found in this neighbor search
  85. self._currentSolution = self._bestSolution
  86. logging.info(
  87. f"End of {type(self).__name__}, best solution found {self._bestSolution}"
  88. )
  89. return self._bestSolution
  90. class HillClimberBestImprovment(Algorithm):
  91. """Hill Climber Best Improvment used as exploitation optimisation algorithm
  92. - First, this algorithm do a neighborhood exploration of a new generated solution (by doing operation on the current solution obtained) in order to find the best solution from the neighborhood space;
  93. - Then replace the best solution found from the neighbordhood space as current solution to use;
  94. - And do these steps until a number of evaluation (stopping criterion) is reached.
  95. Attributes:
  96. initalizer: {function} -- basic function strategy to initialise solution
  97. evaluator: {Evaluator} -- evaluator instance in order to obtained fitness (mono or multiple objectives)
  98. operators: {[Operator]} -- list of operator to use when launching algorithm
  99. policy: {Policy} -- Policy class implementation strategy to select operators
  100. validator: {function} -- basic function to check if solution is valid or not under some constraints
  101. maximise: {bool} -- specify kind of optimisation problem
  102. currentSolution: {Solution} -- current solution managed for current evaluation
  103. bestSolution: {Solution} -- best solution found so far during running algorithm
  104. callbacks: {[Callback]} -- list of Callback class implementation to do some instructions every number of evaluations and `load` when initialising algorithm
  105. parent: {Algorithm} -- parent algorithm reference in case of inner Algorithm instance (optional)
  106. Example:
  107. >>> import random
  108. >>> # operators import
  109. >>> from macop.operators.discrete.crossovers import SimpleCrossover
  110. >>> from macop.operators.discrete.mutators import SimpleMutation
  111. >>> # policy import
  112. >>> from macop.policies.classicals import RandomPolicy
  113. >>> # solution and algorithm
  114. >>> from macop.solutions.discrete import BinarySolution
  115. >>> from macop.algorithms.mono import HillClimberBestImprovment
  116. >>> # evaluator import
  117. >>> from macop.evaluators.discrete.mono import KnapsackEvaluator
  118. >>> # evaluator initialization (worths objects passed into data)
  119. >>> problem_size = 20
  120. >>> worths = [ random.randint(0, 20) for i in range(problem_size) ]
  121. >>> evaluator = KnapsackEvaluator(data={'worths': worths})
  122. >>> # validator specification (based on weights of each objects)
  123. >>> weights = [ random.randint(5, 30) for i in range(problem_size) ]
  124. >>> validator = lambda solution: True if sum([weights[i] for i, value in enumerate(solution.getData()) if value == 1]) < 200 else False
  125. >>> # initialiser function with lambda function
  126. >>> initialiser = lambda x=20: BinarySolution.random(x, validator)
  127. >>> # operators list with crossover and mutation
  128. >>> operators = [SimpleCrossover(), SimpleMutation()]
  129. >>> policy = RandomPolicy(operators)
  130. >>> algo = HillClimberBestImprovment(initialiser, evaluator, operators, policy, validator, maximise=True, verbose=False)
  131. >>> # run the algorithm
  132. >>> solution = algo.run(100)
  133. >>> solution._score
  134. 104
  135. """
  136. def run(self, evaluations):
  137. """
  138. Run the local search algorithm
  139. Args:
  140. evaluations: {int} -- number of Local search evaluations
  141. Returns:
  142. {Solution} -- best solution found
  143. """
  144. # by default use of mother method to initialise variables
  145. super().run(evaluations)
  146. # initialise current solution and best solution
  147. self.initRun()
  148. solutionSize = self._currentSolution._size
  149. # local search algorithm implementation
  150. while not self.stop():
  151. for _ in range(solutionSize):
  152. # update current solution using policy
  153. newSolution = self.update(self._currentSolution)
  154. # if better solution than currently, replace it
  155. if self.isBetter(newSolution):
  156. self._bestSolution = newSolution
  157. # increase number of evaluations
  158. self.increaseEvaluation()
  159. self.progress()
  160. logging.info(
  161. f"---- Current {newSolution} - SCORE {newSolution.fitness()}"
  162. )
  163. # stop algorithm if necessary
  164. if self.stop():
  165. break
  166. # set new current solution using best solution found in this neighbor search
  167. self._currentSolution = self._bestSolution
  168. logging.info(
  169. f"End of {type(self).__name__}, best solution found {self._bestSolution}"
  170. )
  171. return self._bestSolution
  172. class IteratedLocalSearch(Algorithm):
  173. """Iterated Local Search (ILS) used to avoid local optima and increave EvE (Exploration vs Exploitation) compromise
  174. - A number of evaluations (`ls_evaluations`) is dedicated to local search process, here `HillClimberFirstImprovment` algorithm;
  175. - Starting with the new generated solution, the local search algorithm will return a new solution;
  176. - If the obtained solution is better than the best solution known into `IteratedLocalSearch`, then the solution is replaced;
  177. - Restart this process until stopping critirion (number of expected evaluations).
  178. Attributes:
  179. initalizer: {function} -- basic function strategy to initialise solution
  180. evaluator: {function} -- basic function in order to obtained fitness (mono or multiple objectives)
  181. operators: {[Operator]} -- list of operator to use when launching algorithm
  182. policy: {Policy} -- Policy class implementation strategy to select operators
  183. validator: {function} -- basic function to check if solution is valid or not under some constraints
  184. maximise: {bool} -- specify kind of optimisation problem
  185. currentSolution: {Solution} -- current solution managed for current evaluation
  186. bestSolution: {Solution} -- best solution found so far during running algorithm
  187. localSearch: {Algorithm} -- current local search into ILS
  188. callbacks: {[Callback]} -- list of Callback class implementation to do some instructions every number of evaluations and `load` when initialising algorithm
  189. parent: {Algorithm} -- parent algorithm reference in case of inner Algorithm instance (optional)
  190. Example:
  191. >>> import random
  192. >>> # operators import
  193. >>> from macop.operators.discrete.crossovers import SimpleCrossover
  194. >>> from macop.operators.discrete.mutators import SimpleMutation
  195. >>> # policy import
  196. >>> from macop.policies.classicals import RandomPolicy
  197. >>> # solution and algorithm
  198. >>> from macop.solutions.discrete import BinarySolution
  199. >>> from macop.algorithms.mono import IteratedLocalSearch
  200. >>> from macop.algorithms.mono import HillClimberFirstImprovment
  201. >>> # evaluator import
  202. >>> from macop.evaluators.discrete.mono import KnapsackEvaluator
  203. >>> # evaluator initialization (worths objects passed into data)
  204. >>> problem_size = 20
  205. >>> worths = [ random.randint(0, 20) for i in range(problem_size) ]
  206. >>> evaluator = KnapsackEvaluator(data={'worths': worths})
  207. >>> # validator specification (based on weights of each objects)
  208. >>> weights = [ random.randint(5, 30) for i in range(problem_size) ]
  209. >>> validator = lambda solution: True if sum([weights[i] for i, value in enumerate(solution.getData()) if value == 1]) < 200 else False
  210. >>> # initialiser function with lambda function
  211. >>> initialiser = lambda x=20: BinarySolution.random(x, validator)
  212. >>> # operators list with crossover and mutation
  213. >>> operators = [SimpleCrossover(), SimpleMutation()]
  214. >>> policy = RandomPolicy(operators)
  215. >>> local_search = HillClimberFirstImprovment(initialiser, evaluator, operators, policy, validator, maximise=True, verbose=False)
  216. >>> algo = IteratedLocalSearch(initialiser, evaluator, operators, policy, validator, localSearch=local_search, maximise=True, verbose=False)
  217. >>> # run the algorithm
  218. >>> solution = algo.run(100, ls_evaluations=10)
  219. >>> solution._score
  220. 137
  221. """
  222. def __init__(self,
  223. initialiser,
  224. evaluator,
  225. operators,
  226. policy,
  227. validator,
  228. localSearch,
  229. maximise=True,
  230. parent=None,
  231. verbose=True):
  232. """Iterated Local Search Algorithm initialisation with use of specific LocalSearch {Algorithm} instance
  233. Args:
  234. initialiser: {function} -- basic function strategy to initialise solution
  235. evaluator: {Evaluator} -- evaluator instance in order to obtained fitness (mono or multiple objectives)
  236. operators: {[Operator]} -- list of operator to use when launching algorithm
  237. policy: {Policy} -- Policy implementation strategy to select operators
  238. validator: {function} -- basic function to check if solution is valid or not under some constraints
  239. localSearch: {Algorithm} -- current local search into ILS
  240. maximise: {bool} -- specify kind of optimisation problem
  241. parent: {Algorithm} -- parent algorithm reference in case of inner Algorithm instance (optional)
  242. verbose: {bool} -- verbose or not information about the algorithm
  243. """
  244. super().__init__(initialiser, evaluator, operators, policy, validator,
  245. maximise, parent, verbose)
  246. # specific local search associated with current algorithm
  247. self._localSearch = localSearch
  248. # need to attach current algorithm as parent
  249. self._localSearch.setParent(self)
  250. def run(self, evaluations, ls_evaluations=100):
  251. """
  252. Run the iterated local search algorithm using local search (EvE compromise)
  253. Args:
  254. evaluations: {int} -- number of global evaluations for ILS
  255. ls_evaluations: {int} -- number of Local search evaluations (default: 100)
  256. Returns:
  257. {Solution} -- best solution found
  258. """
  259. # by default use of mother method to initialise variables
  260. super().run(evaluations)
  261. # enable resuming for ILS
  262. self.resume()
  263. # initialise current solution
  264. self.initRun()
  265. # add same callbacks
  266. for callback in self._callbacks:
  267. self._localSearch.addCallback(callback)
  268. # local search algorithm implementation
  269. while not self.stop():
  270. # create and search solution from local search
  271. newSolution = self._localSearch.run(ls_evaluations)
  272. # if better solution than currently, replace it
  273. if self.isBetter(newSolution):
  274. self._bestSolution = newSolution
  275. # number of evaluatins increased from LocalSearch
  276. # increase number of evaluations and progress are then not necessary there
  277. #self.increaseEvaluation()
  278. #self.progress()
  279. self.information()
  280. logging.info(
  281. f"End of {type(self).__name__}, best solution found {self._bestSolution}"
  282. )
  283. self.end()
  284. return self._bestSolution