libcrn  3.9.5
A document image processing library
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
CRNGenetic.h
Go to the documentation of this file.
1 
22 #ifndef CRN_GENETIC
23 #define CRN_GENETIC
24 
25 #include <iterator>
26 #include <map>
27 #include <CRNException.h>
28 #include <random>
29 #include <algorithm>
30 
31 namespace crn
32 {
34 
45  template <typename ITER,
46  typename BREEDING_FUNC,
47  typename EVAL_FUNC,
48  typename STOP_FUNC,
49  typename URNG = std::default_random_engine>
50  std::multimap<double, typename std::iterator_traits<ITER>::value_type> Genetic(ITER b, ITER e,
51  BREEDING_FUNC breed,
52  EVAL_FUNC evaluate,
53  STOP_FUNC stop,
55  URNG &&rng = std::default_random_engine{size_t(std::chrono::system_clock::now().time_since_epoch().count())})
56  {
57  using GENOTYPE = typename std::iterator_traits<ITER>::value_type;
58 
59  // evaluate the initial population
60  auto population = std::multimap<double, GENOTYPE>{};
61  for (; b < e; ++b)
62  population.emplace(evaluate(*b), *b);
63  if (population.size() < 2)
64  throw ExceptionLogic("Parthenogenesis is not allowed, at least two individuals are needed.");
65 
66  // iterate generations
67  while (!stop(population))
68  {
69  auto newpopulation = std::multimap<double, GENOTYPE>{};
70  //auto it = population.cbegin();
71  // create a randomized order of the population
72  auto ranpos = std::vector<size_t>(population.size());
73  std::iota(ranpos.begin(), ranpos.end(), 0);
74  std::shuffle(ranpos.begin(), ranpos.end(), rng);
75  // breeding loop
76  for (size_t tmp = 0; tmp < population.size(); )
77  {
78  if (tmp + 1 >= ranpos.size()) // if the number of individuals is odd, the least fitting individual is not bred :o(
79  break;
80  // pick two random individuals
81  auto it1 = std::next(population.begin(), ranpos[tmp++]);
82  auto it2 = std::next(population.begin(), ranpos[tmp++]);
83  auto children = std::pair<GENOTYPE, GENOTYPE>{};
84  if (tmp >= ranpos.size())
85  { // if cannot pick a third, just breed what we have since they are the last remaining individuals.
86  children = breed(it1->second, it2->second, rng);
87  }
88  else
89  { // pick a third random individual
90  auto it3 = std::next(population.begin(), ranpos[tmp]);
91  // breed the best two, the least fitting one will be part of next love triangle
92  if (it1->first < it2->first)
93  {
94  if (it2->first < it3->first)
95  {
96  children = breed(it1->second, it2->second, rng);
97  // third individual's index stays in place in the list to be reused in next loop
98  }
99  else
100  {
101  children = breed(it1->second, it3->second, rng);
102  ranpos[tmp] = ranpos[tmp - 1]; // second individual's index stored for next loop
103  }
104  }
105  else
106  {
107  if (it1->first < it3->first)
108  {
109  children = breed(it1->second, it2->second, rng);
110  // third individual's index stays in place in the list to be reused in next loop
111  }
112  else
113  {
114  children = breed(it2->second, it3->second, rng);
115  ranpos[tmp] = ranpos[tmp - 2]; // first individual's index stored for next loop
116  }
117  }
118  }
119 
120  // evaluate children
121  auto fitness = evaluate(children.first);
122  newpopulation.emplace(fitness, std::move(children.first));
123  fitness = evaluate(children.second);
124  newpopulation.emplace(fitness, std::move(children.second));
125  } // breeding loop
126 
127  if (keep_parents == GenerationStrategy::KEEP_BEST_PARENT)
128  { // keep only the best parent
129  newpopulation.insert(*population.begin());
130  newpopulation.erase(--newpopulation.end());
131  population.swap(newpopulation);
132  }
133  else
134  { // keep the best individuals, parents and children included
135  const auto s = population.size();
136  std::move(newpopulation.begin(), newpopulation.end(), std::inserter(population, population.end()));
137  population.erase(std::next(population.begin(), s), population.end());
138  }
139 
140  }
141  return population;
142  }
143 
145  // Breeding functors
147 
152  struct CrossOver
153  {
161  template<typename T, typename URNG> inline std::pair<std::vector<T>, std::vector<T>> operator()(const std::vector<T> &idv1, const std::vector<T> &idv2, URNG &rng) const
162  {
163  if (idv1.size() != idv2.size())
164  throw ExceptionDimension("The individuals must have the same size.");
165  const auto s = idv1.size();
166  if (!s)
167  throw ExceptionInvalidArgument("The individuals must not be empty.");
168 
169  auto ran = std::uniform_int_distribution<size_t>(0, s - 1);
170  const auto cut = ran(rng); // position of the cut
171  auto child1 = std::vector<T>(s);
172  auto child2 = std::vector<T>(s);
173  for (auto tmp = size_t(0); tmp < cut; ++tmp)
174  { // copy first string
175  child1[tmp] = idv1[tmp];
176  child2[tmp] = idv2[tmp];
177  }
178  for (auto tmp = cut; tmp < s; ++tmp)
179  { // copy second string from the other parent
180  child1[tmp] = idv2[tmp];
181  child2[tmp] = idv1[tmp];
182  }
183  return std::make_pair(std::move(child1), std::move(child2));
184  }
185  };
186 
188  // Stop functors
190 
194  {
195  constexpr GenerationCounter(size_t cnt):generation(cnt) {}
196  template<typename T> inline bool operator()(const T &)
197  { return --generation == 0; }
198  private:
199  size_t generation;
200  };
201 
205  {
206  constexpr FitnessThreshold(double thresh):threshold(thresh) {}
207  template<typename T> inline bool operator()(const std::multimap<double, T> &population) const
208  { return population.begin()->first < threshold; }
209  private:
210  double threshold;
211  };
212 }
213 #endif
214 
Stops when the best individual has a fitness lower than a threshold.
Definition: CRNGenetic.h:204
bool operator()(const std::multimap< double, T > &population) const
Definition: CRNGenetic.h:207
constexpr GenerationCounter(size_t cnt)
Definition: CRNGenetic.h:195
Simple counter to stop an genetic algorithm.
Definition: CRNGenetic.h:193
bool operator()(const T &)
Definition: CRNGenetic.h:196
GenerationStrategy
Definition: CRNGenetic.h:33
constexpr FitnessThreshold(double thresh)
Definition: CRNGenetic.h:206
A dimension error.
Definition: CRNException.h:119
Crossover functor.
Definition: CRNGenetic.h:152
std::pair< std::vector< T >, std::vector< T > > operator()(const std::vector< T > &idv1, const std::vector< T > &idv2, URNG &rng) const
Definition: CRNGenetic.h:161
std::multimap< double, typename std::iterator_traits< ITER >::value_type > Genetic(ITER b, ITER e, BREEDING_FUNC breed, EVAL_FUNC evaluate, STOP_FUNC stop, GenerationStrategy keep_parents=GenerationStrategy::KEEP_BEST_PARENT, URNG &&rng=std::default_random_engine{size_t(std::chrono::system_clock::now().time_since_epoch().count())})
Definition: CRNGenetic.h:50
Invalid argument error (e.g.: nullptr pointer)
Definition: CRNException.h:107