crossovers.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. """Crossover implementations for continuous solutions kind
  2. """
  3. # main imports
  4. import random
  5. import sys
  6. import numpy as np
  7. # module imports
  8. from macop.operators.base import Crossover
  9. class BasicDifferentialEvolutionCrossover(Crossover):
  10. """Basic Differential Evolution implementation for continuous solution
  11. Attributes:
  12. kind: {:class:`~macop.operators.base.KindOperator`} -- specify the kind of operator
  13. Example:
  14. >>> # import of solution and polynomial mutation operator
  15. >>> from macop.solutions.continuous import ContinuousSolution
  16. >>> from macop.operators.continuous.crossovers import BasicDifferentialEvolutionCrossover
  17. >>> solution = ContinuousSolution.random(5, (-2, 2))
  18. >>> list(solution.data)
  19. [-1.3760219186551894, -1.7676655513272022, 1.4647045830997407, 0.4044600469728352, 0.832290311184182]
  20. >>> crossover = BasicDifferentialEvolutionCrossover(interval=(-2, 2))
  21. >>> crossover_solution = crossover.apply(solution)
  22. >>> list(crossover_solution.data)
  23. [-1.7016619497704522, -0.43633033292228895, 2.0, -0.034751768954844, 0.6134819652022994]
  24. """
  25. def __init__(self, interval, CR=1.0, F=0.5):
  26. """"Basic Differential Evolution crossover initialiser in order to specify kind of Operator and interval of continuous solution
  27. Args:
  28. interval: {(float, float)} -- minimum and maximum values interval of variables in the solution
  29. CR: {float} -- probability to use of new generated solutions when modifying a value of current solution
  30. F: {float} -- degree of impact of the new generated solutions on the current solution when obtaining new solution
  31. """
  32. super().__init__()
  33. self.mini, self.maxi = interval
  34. self.CR = CR
  35. self.F = F
  36. def apply(self, solution1, solution2=None):
  37. """Create new solution based on solution passed as parameter
  38. Args:
  39. solution1: {:class:`~macop.solutions.base.Solution`} -- the first solution to use for generating new solution
  40. solution2: {:class:`~macop.solutions.base.Solution`} -- the second solution to use for generating new solution
  41. Returns:
  42. {:class:`~macop.solutions.base.Solution`}: new continuous generated solution
  43. """
  44. size = solution1.size
  45. solution1 = solution1.clone()
  46. # create two new random solutions using instance and its static method
  47. solution2 = solution1.random(size, interval=(self.mini, self.maxi))
  48. solution3 = solution1.random(size, interval=(self.mini, self.maxi))
  49. # apply crossover on the new computed solution
  50. for i in range(len(solution1.data)):
  51. # use of CR to change or not the current value of the solution using new solutions
  52. if random.uniform(0, 1) < self.CR:
  53. solution1.data[i] = solution1.data[i] + self.F * (solution2.data[i] - solution3.data[i])
  54. # repair solution if necessary
  55. solution1.data = self._repair(solution1)
  56. return solution1
  57. def _repair(self, solution):
  58. """
  59. Private repair function for solutions if an element is out of bounds of an expected interval
  60. Args:
  61. solution: {:class:`~macop.solutions.base.Solution`} -- the solution to use for generating new solution
  62. Returns:
  63. {ndarray} -- repaired array of float values
  64. """
  65. return np.array([self.mini if x < self.mini else self.maxi if x > self.maxi else x for x in solution.data])