crossovers.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. """Crossover implementations for discrete solutions kind
  2. """
  3. # main imports
  4. import random
  5. import sys
  6. # module imports
  7. from ..base import Crossover
  8. class SimpleCrossover(Crossover):
  9. """Crossover implementation which generated new solution by splitting at mean size best solution and current solution
  10. Attributes:
  11. kind: {Algorithm} -- specify the kind of operator
  12. Example:
  13. >>> # operators import
  14. >>> from macop.operators.discrete.crossovers import SimpleCrossover
  15. >>> from macop.operators.discrete.mutators import SimpleMutation
  16. >>> # policy import
  17. >>> from macop.policies.reinforcement import UCBPolicy
  18. >>> # solution and algorithm
  19. >>> from macop.solutions.discrete import BinarySolution
  20. >>> from macop.algorithms.mono import IteratedLocalSearch
  21. >>> from macop.algorithms.mono import HillClimberFirstImprovment
  22. >>> # evaluator import
  23. >>> from macop.evaluators.discrete.mono import KnapsackEvaluator
  24. >>> # evaluator initialization (worths objects passed into data)
  25. >>> worths = [ random.randint(0, 20) for i in range(10) ]
  26. >>> evaluator = KnapsackEvaluator(data={'worths': worths})
  27. >>> # validator specification (based on weights of each objects)
  28. >>> weights = [ random.randint(20, 30) for i in range(10) ]
  29. >>> validator = lambda solution: True if sum([weights[i] for i, value in enumerate(solution._data) if value == 1]) < 200 else False
  30. >>> # initializer function with lambda function
  31. >>> initializer = lambda x=10: BinarySolution.random(x, validator)
  32. >>> # operators list with crossover and mutation
  33. >>> simple_crossover = SimpleCrossover()
  34. >>> simple_mutation = SimpleMutation()
  35. >>> operators = [simple_crossover, simple_mutation]
  36. >>> policy = UCBPolicy(operators)
  37. >>> local_search = HillClimberFirstImprovment(initializer, evaluator, operators, policy, validator, maximise=True, verbose=False)
  38. >>> algo = IteratedLocalSearch(initializer, evaluator, operators, policy, validator, localSearch=local_search, maximise=True, verbose=False)
  39. >>> # using best solution, simple crossover is applied
  40. >>> best_solution = algo.run(100)
  41. >>> list(best_solution._data)
  42. [1, 1, 0, 1, 0, 1, 1, 1, 0, 1]
  43. >>> new_solution_1 = initializer()
  44. >>> new_solution_2 = initializer()
  45. >>> offspring_solution = simple_crossover.apply(new_solution_1, new_solution_2)
  46. >>> list(offspring_solution._data)
  47. [0, 1, 1, 0, 1, 0, 1, 1, 0, 1]
  48. """
  49. def apply(self, solution1, solution2=None):
  50. """Create new solution based on best solution found and solution passed as parameter
  51. Args:
  52. solution1: {Solution} -- the first solution to use for generating new solution
  53. solution2: {Solution} -- the second solution to use for generating new solution
  54. Returns:
  55. {Solution} -- new generated solution
  56. """
  57. size = solution1._size
  58. # copy data of solution
  59. firstData = solution1._data.copy()
  60. # get best solution from current algorithm
  61. if solution2 is None:
  62. copy_solution = self._algo._bestSolution.clone()
  63. else:
  64. copy_solution = solution2.clone()
  65. splitIndex = int(size / 2)
  66. if random.uniform(0, 1) > 0.5:
  67. copy_solution._data[splitIndex:] = firstData[splitIndex:]
  68. else:
  69. copy_solution._data[:splitIndex] = firstData[:splitIndex]
  70. return copy_solution
  71. class RandomSplitCrossover(Crossover):
  72. """Crossover implementation which generated new solution by randomly splitting best solution and current solution
  73. Attributes:
  74. kind: {KindOperator} -- specify the kind of operator
  75. Example:
  76. >>> # operators import
  77. >>> from macop.operators.discrete.crossovers import RandomSplitCrossover
  78. >>> from macop.operators.discrete.mutators import SimpleMutation
  79. >>> # policy import
  80. >>> from macop.policies.reinforcement import UCBPolicy
  81. >>> # solution and algorithm
  82. >>> from macop.solutions.discrete import BinarySolution
  83. >>> from macop.algorithms.mono import IteratedLocalSearch
  84. >>> from macop.algorithms.mono import HillClimberFirstImprovment
  85. >>> # evaluator import
  86. >>> from macop.evaluators.discrete.mono import KnapsackEvaluator
  87. >>> # evaluator initialization (worths objects passed into data)
  88. >>> worths = [ random.randint(0, 20) for i in range(10) ]
  89. >>> evaluator = KnapsackEvaluator(data={'worths': worths})
  90. >>> # validator specification (based on weights of each objects)
  91. >>> weights = [ random.randint(20, 30) for i in range(10) ]
  92. >>> validator = lambda solution: True if sum([weights[i] for i, value in enumerate(solution._data) if value == 1]) < 200 else False
  93. >>> # initializer function with lambda function
  94. >>> initializer = lambda x=10: BinarySolution.random(x, validator)
  95. >>> # operators list with crossover and mutation
  96. >>> random_split_crossover = RandomSplitCrossover()
  97. >>> simple_mutation = SimpleMutation()
  98. >>> operators = [random_split_crossover, simple_mutation]
  99. >>> policy = UCBPolicy(operators)
  100. >>> local_search = HillClimberFirstImprovment(initializer, evaluator, operators, policy, validator, maximise=True, verbose=False)
  101. >>> algo = IteratedLocalSearch(initializer, evaluator, operators, policy, validator, localSearch=local_search, maximise=True, verbose=False)
  102. >>> # using best solution, simple crossover is applied
  103. >>> best_solution = algo.run(100)
  104. >>> list(best_solution._data)
  105. [1, 1, 1, 0, 1, 0, 1, 1, 1, 0]
  106. >>> new_solution_1 = initializer()
  107. >>> new_solution_2 = initializer()
  108. >>> offspring_solution = random_split_crossover.apply(new_solution_1, new_solution_2)
  109. >>> list(offspring_solution._data)
  110. [0, 0, 0, 1, 1, 0, 0, 1, 0, 0]
  111. """
  112. def apply(self, solution1, solution2=None):
  113. """Create new solution based on best solution found and solution passed as parameter
  114. Args:
  115. solution1: {Solution} -- the first solution to use for generating new solution
  116. solution2: {Solution} -- the second solution to use for generating new solution
  117. Returns:
  118. {Solution} -- new generated solution
  119. """
  120. size = solution1._size
  121. # copy data of solution
  122. firstData = solution1._data.copy()
  123. # get best solution from current algorithm
  124. if solution2 is None:
  125. copy_solution = self._algo._bestSolution.clone()
  126. else:
  127. copy_solution = solution2.clone()
  128. splitIndex = random.randint(0, size)
  129. if random.uniform(0, 1) > 0.5:
  130. copy_solution._data[splitIndex:] = firstData[splitIndex:]
  131. else:
  132. copy_solution._data[:splitIndex] = firstData[:splitIndex]
  133. return copy_solution