123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169 |
- """Crossover implementations for discrete solutions kind
- """
- # main imports
- import random
- import sys
- # module imports
- from macop.operators.base import Crossover
- class SimpleCrossover(Crossover):
- """Crossover implementation which generated new solution by splitting at mean size best solution and current solution
- Attributes:
- kind: {:class:`~macop.algorithms.base.Algorithm`} -- specify the kind of operator
- Example:
- >>> # operators import
- >>> from macop.operators.discrete.crossovers import SimpleCrossover
- >>> from macop.operators.discrete.mutators import SimpleMutation
- >>>
- >>> # policy import
- >>> from macop.policies.reinforcement import UCBPolicy
- >>>
- >>> # solution and algorithm imports
- >>> from macop.solutions.discrete import BinarySolution
- >>> from macop.algorithms.mono import IteratedLocalSearch
- >>> from macop.algorithms.mono import HillClimberFirstImprovment
- >>>
- >>> # evaluator import
- >>> from macop.evaluators.discrete.mono import KnapsackEvaluator
- >>>
- >>> # evaluator initialization (worths objects passed into data)
- >>> worths = [ random.randint(0, 20) for i in range(10) ]
- >>> evaluator = KnapsackEvaluator(data={'worths': worths})
- >>>
- >>> # validator specification (based on weights of each objects)
- >>> weights = [ random.randint(20, 30) for i in range(10) ]
- >>> validator = lambda solution: True if sum([weights[i] for i, value in enumerate(solution.data) if value == 1]) < 200 else False
- >>>
- >>> # initialiser function for binary solution using specific solution size
- >>> initialiser = lambda x=10: BinarySolution.random(x, validator)
- >>>
- >>> # operators list with crossover and mutation
- >>> simple_crossover = SimpleCrossover()
- >>> simple_mutation = SimpleMutation()
- >>> operators = [simple_crossover, simple_mutation]
- >>> policy = UCBPolicy(operators)
- >>> local_search = HillClimberFirstImprovment(initialiser, evaluator, operators, policy, validator, maximise=True, verbose=False)
- >>> algo = IteratedLocalSearch(initialiser, evaluator, operators, policy, validator, localSearch=local_search, maximise=True, verbose=False)
- >>>
- >>> # using best solution, simple crossover is applied
- >>> best_solution = algo.run(100)
- >>> list(best_solution.data)
- [1, 1, 0, 1, 1, 1, 0, 1, 1, 1]
- >>> new_solution_1 = initialiser()
- >>> new_solution_2 = initialiser()
- >>> offspring_solution = simple_crossover.apply(new_solution_1, new_solution_2)
- >>> list(offspring_solution.data)
- [1, 0, 1, 1, 0, 0, 0, 1, 0, 0]
- """
- def apply(self, solution1, solution2=None):
- """Create new solution based on best solution found and solution passed as parameter
- Args:
- solution1: {:class:`~macop.solutions.base.Solution`} -- the first solution to use for generating new solution
- solution2: {:class:`~macop.solutions.base.Solution`} -- the second solution to use for generating new solution
- Returns:
- {:class:`~macop.solutions.base.Solution`}: new generated solution
- """
- size = solution1.size
- # copy data of solution
- firstData = solution1.data.copy()
- # copy of solution2 as output solution
- copy_solution = solution2.clone()
- splitIndex = int(size / 2)
- if random.uniform(0, 1) > 0.5:
- copy_solution.data[splitIndex:] = firstData[splitIndex:]
- else:
- copy_solution.data[:splitIndex] = firstData[:splitIndex]
- return copy_solution
- class RandomSplitCrossover(Crossover):
- """Crossover implementation which generated new solution by randomly splitting best solution and current solution
- Attributes:
- kind: {:class:`~macop.operators.base.KindOperator`} -- specify the kind of operator
- Example:
- >>> # operators import
- >>> from macop.operators.discrete.crossovers import RandomSplitCrossover
- >>> from macop.operators.discrete.mutators import SimpleMutation
- >>>
- >>> # policy import
- >>> from macop.policies.reinforcement import UCBPolicy
- >>>
- >>> # solution and algorithm imports
- >>> from macop.solutions.discrete import BinarySolution
- >>> from macop.algorithms.mono import IteratedLocalSearch
- >>> from macop.algorithms.mono import HillClimberFirstImprovment
- >>>
- >>> # evaluator import
- >>> from macop.evaluators.discrete.mono import KnapsackEvaluator
- >>>
- >>> # evaluator initialization (worths objects passed into data)
- >>> worths = [ random.randint(0, 20) for i in range(10) ]
- >>> evaluator = KnapsackEvaluator(data={'worths': worths})
- >>>
- >>> # validator specification (based on weights of each objects)
- >>> weights = [ random.randint(20, 30) for i in range(10) ]
- >>> validator = lambda solution: True if sum([weights[i] for i, value in enumerate(solution.data) if value == 1]) < 200 else False
- >>>
- >>> # initialiser function for binary solution using specific solution size
- >>> initialiser = lambda x=10: BinarySolution.random(x, validator)
- >>>
- >>> # operators list with crossover and mutation
- >>> random_split_crossover = RandomSplitCrossover()
- >>> simple_mutation = SimpleMutation()
- >>> operators = [random_split_crossover, simple_mutation]
- >>> policy = UCBPolicy(operators)
- >>> local_search = HillClimberFirstImprovment(initialiser, evaluator, operators, policy, validator, maximise=True, verbose=False)
- >>> algo = IteratedLocalSearch(initialiser, evaluator, operators, policy, validator, localSearch=local_search, maximise=True, verbose=False)
- >>>
- >>> # using best solution, simple crossover is applied
- >>> best_solution = algo.run(100)
- >>> list(best_solution.data)
- [1, 1, 1, 1, 1, 0, 1, 1, 0, 0]
- >>> new_solution_1 = initialiser()
- >>> new_solution_2 = initialiser()
- >>> offspring_solution = random_split_crossover.apply(new_solution_1, new_solution_2)
- >>> list(offspring_solution.data)
- [0, 0, 0, 1, 1, 1, 1, 0, 0, 1]
- """
- def apply(self, solution1, solution2=None):
- """Create new solution based on best solution found and solution passed as parameter
- Args:
- solution1: {:class:`~macop.solutions.base.Solution`} -- the first solution to use for generating new solution
- solution2: {:class:`~macop.solutions.base.Solution`} -- the second solution to use for generating new solution
- Returns:
- {:class:`~macop.solutions.base.Solution`}: new generated solution
- """
- size = solution1.size
- # copy data of solution
- firstData = solution1.data.copy()
- # copy of solution2 as output solution
- copy_solution = solution2.clone()
- splitIndex = random.randint(0, size)
- if random.uniform(0, 1) > 0.5:
- copy_solution.data[splitIndex:] = firstData[splitIndex:]
- else:
- copy_solution.data[:splitIndex] = firstData[:splitIndex]
- return copy_solution
|