GENEIAL  0.2=/
 All Classes Pages
RouletteWheelSelection.hpp
1 #pragma once
2 
3 #include <geneial/core/operations/selection/RouletteWheelSelection.h>
4 #include <geneial/core/population/chromosome/BaseChromosome.h>
5 #include <geneial/utility/Random.h>
6 
7 #include <vector>
8 #include <algorithm>
9 #include <cassert>
10 #include <utility>
11 
12 geneial_private_namespace(geneial)
13 {
14 geneial_private_namespace(operation)
15 {
16 geneial_private_namespace(selection)
17 {
18 
19 using ::geneial::population::Population;
20 using ::geneial::population::management::BaseManager;
21 using ::geneial::utility::Random;
22 
23 geneial_export_namespace
24 {
25 
26 //TODO (bewo): This is not very efficient for large populations.
27 
28 template<typename FITNESS_TYPE>
29 class RouletteWheel
30 {
31 
32 private:
33 
34  typedef typename BaseChromosome<FITNESS_TYPE>::ptr chrom_ptr_type;
35 
36  typedef typename Population<FITNESS_TYPE>::fitnessmap_const_it const_pop_itr;
37 
38  FITNESS_TYPE _sum;
39 
40  //NOTE (bewo): Benchmarked this against a map, however the RBTree and all the recurring mallocs are very costly, hence vector.
41  std::vector<std::pair<FITNESS_TYPE, chrom_ptr_type>> _ranges;
42 
43  struct RouletteWheelComparator
44  {
45  bool operator()(const std::pair<FITNESS_TYPE, chrom_ptr_type>& v1,
46  const std::pair<FITNESS_TYPE, chrom_ptr_type>& v2) const
47  {
48  return v1.first < v2.first;
49  }
50 
51  bool operator()(const std::pair<FITNESS_TYPE, chrom_ptr_type>& v1, const FITNESS_TYPE v2) const
52  {
53  return v1.first < v2;
54  }
55 
56  bool operator()(const FITNESS_TYPE v1, const FITNESS_TYPE v2) const
57  {
58  return v1 < v2;
59  }
60 
61  bool operator()(const FITNESS_TYPE v1, const std::pair<FITNESS_TYPE, chrom_ptr_type>& v2) const
62  {
63  return v1 < v2.first;
64  }
65  };
66 
67 public:
68  //minimal place on the wheel
69  constexpr const static FITNESS_TYPE CONST_INC_BY = 1;
70 
71  RouletteWheel(const Population<FITNESS_TYPE> &population) :
72  _sum(0)
73  {
74 
75  _ranges.reserve(population.getSize());
76 
77  //Worst value:
78  const FITNESS_TYPE worstFitness = population.getFitnessMap().cbegin()->first;
79  FITNESS_TYPE positiveTranslation = 0;
80 
81  if(worstFitness <= 0)
82  {
83  positiveTranslation = std::abs(worstFitness);
84  }
85 
86  for (const auto &it : population.getFitnessMap())
87  {
88  _sum += CONST_INC_BY;
89 
90  if(it.first < 0)
91  {
92  _sum += std::abs(it.first);
93  }
94  else
95  {
96  //In case we had negative fitness values in the population,
97  //we offset of the positive fitness values by the amount of the worst negative fitness,
98  //so that the positive fitness values have higher probability than the negative ones.
99  _sum += positiveTranslation + it.first;
100  }
101 
102  _ranges.emplace_back(_sum, it.second);
103  }
104  }
105 
106  chrom_ptr_type spin(double random)
107  {
108  return std::lower_bound(_ranges.begin(), _ranges.end(),
109  static_cast<FITNESS_TYPE>(random * _sum),RouletteWheelComparator())->second;
110  }
111 
112 
113 };
114 
115 template<typename FITNESS_TYPE>
116 typename BaseSelectionOperation<FITNESS_TYPE>::selection_result_set RouletteWheelSelection<FITNESS_TYPE>::doSelect(
117  const Population<FITNESS_TYPE> &population, BaseManager<FITNESS_TYPE> &manager) const
118 {
119 
120  //shorthands for type mess
121  typedef typename BaseSelectionOperation<FITNESS_TYPE>::selection_result_set result_set;
122  typedef typename BaseChromosome<FITNESS_TYPE>::ptr chrom_ptr_type;
123 
124  result_set result;
125 
126  unsigned int left_select = this->getSettings().getNumberOfParents();
127 
128  RouletteWheel<FITNESS_TYPE> rouletteWheel(population);
129 
130  //TODO (bewo) allow parameter for the best chromosomes to be selected (and skipped here)
131  assert(population.getSize() >= left_select);
132 
133  while (left_select > 0)
134  {
135  //TODO (bewo) make this a RouletteWheel setting:
136  const bool allowDuplicates = false;
137  chrom_ptr_type ptr;
138  do
139  {
140  //TODO (bewo) Benchmark this for duplicates
141  const double random = Random::generate<double>(0.0, 1.0);
142  ptr = rouletteWheel.spin(random);
143  } while (allowDuplicates || std::find(result.begin(), result.end(), ptr) != result.end());
144  left_select--;
145  result.emplace_back(ptr);
146  }
147  return result;
148 }
149 
150 } /* geneial_export_namespace */
151 } /* private namespace selection */
152 } /* private namespace operation */
153 } /* private namespace geneial */