mc2lib
simplega.hpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014-2016, Marco Elver
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *
12  * * Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in the
14  * documentation and/or other materials provided with the
15  * distribution.
16  *
17  * * Neither the name of the software nor the names of its contributors
18  * may be used to endorse or promote products derived from this
19  * software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33 
34 #ifndef MC2LIB_SIMPLEGA_HPP_
35 #define MC2LIB_SIMPLEGA_HPP_
36 
37 #include <algorithm>
38 #include <cassert>
39 #include <cstddef>
40 #include <list>
41 #include <random>
42 #include <sstream>
43 #include <string>
44 #include <unordered_set>
45 #include <utility>
46 #include <vector>
47 
48 namespace mc2lib {
49 
54 namespace simplega {
55 
60 namespace evolve {
61 
69 template <class URNG, class GenomeT, class C, bool one_point = false,
70  bool theone = false>
71 inline void CutSpliceMutate(URNG& urng, const GenomeT& mate1,
72  const GenomeT& mate2, float mutation_rate,
73  C* container) {
74  assert(!mate1.get().empty());
75  assert(!mate2.get().empty());
76 
77  std::uniform_int_distribution<std::size_t> dist1(0, mate1.get().size() - 1);
78  std::uniform_int_distribution<std::size_t> dist2(0, mate2.get().size() - 1);
79 
80  const std::size_t cut1 = dist1(urng);
81  const std::size_t cut2 = one_point ? cut1 : dist2(urng);
82 
83  // a[0:cut_a] + b[cut_b:]
84  auto cut_and_splice = [](const GenomeT& a, const GenomeT& b,
85  std::size_t cut_a, std::size_t cut_b) {
86  auto result = a.get();
87  auto ita = result.begin();
88  std::advance(ita, cut_a);
89  result.erase(ita, result.end());
90  auto itb = b.get().begin();
91  std::advance(itb, cut_b);
92  result.insert(result.end(), itb, b.get().end());
93  return GenomeT(a, b, result);
94  };
95 
96  // child1 = mate1[0:cut1] + mate2[cut2:]
97  GenomeT child1 = cut_and_splice(mate1, mate2, cut1, cut2);
98  if (child1.get().size()) {
99  child1.Mutate(mutation_rate);
100  container->push_back(std::move(child1));
101  }
102 
103  if (!theone) {
104  // child2 = mate2[0:cut2] + mate1[cut1:]
105  GenomeT child2 = cut_and_splice(mate2, mate1, cut2, cut1);
106  if (child2.get().size()) {
107  child2.Mutate(mutation_rate);
108  container->push_back(std::move(child2));
109  }
110  }
111 }
112 
113 } // namespace evolve
114 
128 template <class T>
129 class Genome {
130  public:
131  typedef std::vector<T> Container;
132 
138  Genome() {}
139 
145  explicit Genome(Container g) : genome_(std::move(g)) {}
146 
147  virtual ~Genome() {}
148 
154  const Container& get() const { return genome_; }
155 
161  Container* get_ptr() { return &genome_; }
162 
171  virtual bool operator<(const Genome& rhs) const {
172  return Fitness() > rhs.Fitness();
173  }
174 
180  virtual void Mutate(float rate) = 0;
181 
187  virtual float Fitness() const = 0;
188 
195  virtual operator float() const { return Fitness(); }
196 
200  virtual operator std::string() const {
201  std::ostringstream oss;
202  oss << "[";
203  bool first = true;
204  for (const auto& v : genome_) {
205  oss << (first ? "" : ", ") << v;
206  first = false;
207  }
208  oss << "]";
209  return oss.str();
210  }
211 
212  protected:
216  Container genome_;
217 };
218 
225 template <class GenomeT>
226 class GenePool {
227  public:
232  typedef std::list<GenomeT> Population;
233  typedef std::vector<GenomeT*> Selection;
234 
235  explicit GenePool(std::size_t target_population_size = 80,
236  float mutation_rate = 0.02f)
237  : target_population_size_(target_population_size),
238  mutation_rate_(mutation_rate),
239  steps_(0) {
240  // mutation rate is a percentage
241  if (mutation_rate_ > 1.0f) {
242  mutation_rate_ = 1.0f;
243  } else if (mutation_rate_ < 0.0f) {
244  mutation_rate_ = 0.0f;
245  }
246 
247  // Initialize with defaults (e.g. random)
248  population_.resize(target_population_size_);
249  }
250 
251  explicit GenePool(Population population, float mutation_rate = 0.02f)
252  : target_population_size_(population.size()),
253  mutation_rate_(mutation_rate),
254  steps_(0),
255  population_(std::move(population)) {}
256 
257  explicit GenePool(Selection selection, float mutation_rate = 0.02f)
258  : target_population_size_(selection.size()),
259  mutation_rate_(mutation_rate),
260  steps_(0) {
261  for (const auto& gptr : selection) {
262  population_.push_back(*gptr);
263  }
264  }
265 
266  operator std::string() const {
267  std::ostringstream oss;
268  oss << "{ ";
269 
270  bool first = true;
271  for (const auto& genome : population_) {
272  oss << (first ? "" : ", ") << static_cast<std::string>(genome);
273  first = false;
274  }
275 
276  oss << " }";
277  return oss.str();
278  }
279 
280  const Population& get() const { return population_; }
281 
282  Population* get_ptr() { return &population_; }
283 
284  float mutation_rate() const { return mutation_rate_; }
285 
286  void set_mutation_rate(float mutation_rate) {
287  mutation_rate_ = mutation_rate;
288  }
289 
290  std::size_t target_population_size() const { return target_population_size_; }
291 
292  std::size_t population_size() const { return population_.size(); }
293 
294  std::size_t steps() const { return steps_; }
295 
296  float AverageFitness() const {
297  float result = 0.0f;
298  for (const auto& g : population_) {
299  result += g.Fitness();
300  }
301  return result / population_.size();
302  }
303 
304  float WorstFitness() const {
305  return std::max_element(population_.begin(), population_.end())->Fitness();
306  }
307 
308  float BestFitness() const {
309  return std::min_element(population_.begin(), population_.end())->Fitness();
310  }
311 
315  void Sort() { population_.Sort(); }
316 
317  const GenomeT& SelectBest() const {
318  return *std::min_element(population_.begin(), population_.end());
319  }
320 
324  Selection SelectAll() {
325  Selection result;
326  result.reserve(population_.size());
327  for (GenomeT& g : population_) {
328  result.push_back(&g);
329  }
330  return result;
331  }
332 
336  template <class URNG, class DIST>
337  Selection SelectDist(URNG& urng, DIST& dist, std::size_t count) {
338  assert(population_.size() >= count);
339 
340  std::unordered_set<std::size_t> used;
341  Selection result;
342  result.reserve(count);
343 
344  while (result.size() < count) {
345  std::size_t idx = dist(urng);
346  if (used.find(idx) != used.end()) {
347  continue;
348  }
349 
350  auto it = population_.begin();
351  std::advance(it, idx);
352  result.push_back(&(*it));
353  used.insert(idx);
354  }
355 
356  return result;
357  }
358 
363  template <class URNG>
364  Selection SelectRoulette(URNG& urng, std::size_t count) {
365  std::discrete_distribution<std::size_t> dist(population_.begin(),
366  population_.end());
367  return SelectDist(urng, dist, count);
368  }
369 
374  template <class URNG>
375  Selection SelectUniform(URNG& urng, std::size_t count) {
376  std::uniform_int_distribution<std::size_t> dist(0, population_.size() - 1);
377  return SelectDist(urng, dist, count);
378  }
379 
383  void SelectionSort(Selection* v) {
384  std::sort(v->begin(), v->end(), [](const GenomeT* lhs, const GenomeT* rhs) {
385  return (*lhs) < (*rhs);
386  });
387  }
388 
396  template <class URNG, class CrossoverMutateFunc>
397  void Step(URNG& urng, CrossoverMutateFunc crossover_mutate,
398  const Selection& selection, std::size_t mates, std::size_t keep = 0,
399  std::size_t mate1_stride = 1, std::size_t mate2_stride = 1) {
400  assert(selection.size() >= mates);
401  assert(selection.size() >= keep);
402 
403  std::size_t replace = selection.size() - keep;
404  assert(replace > 0);
405 
406  const std::size_t target_population_size =
407  target_population_size_ + replace;
408 
409  // Add offspring
410  for (std::size_t i = 0; i < mates; i += mate1_stride) {
411  const auto mate1 = selection[i];
412 
413  // j = i: avoid mating 2 individuals twice
414  for (std::size_t j = i + 1; j < mates; j += mate2_stride) {
415  const auto mate2 = selection[j];
416 
417  crossover_mutate(urng, *mate1, *mate2, mutation_rate_, &population_);
418 
419  if (population_.size() >= target_population_size) {
420  goto target_reached;
421  }
422  }
423  }
424  target_reached:
425 
426  // Remove selection[keep:]
427  auto selection_start = selection.begin();
428  std::advance(selection_start, keep);
429 
430  for (auto it = population_.begin();
431  it != population_.end() && replace > 0;) {
432  const GenomeT* val = &(*it);
433  auto match = std::find(selection_start, selection.end(), val);
434 
435  if (match != selection.end()) {
436  if (match == selection_start) {
437  ++selection_start;
438  }
439 
440  it = population_.erase(it);
441  --replace;
442  } else {
443  ++it;
444  }
445  }
446 
447  assert(population_.size() >= target_population_size_);
448  // The population might be larger than the target, if crossover
449  // generates more than one offspring.
450 
451  ++steps_;
452  }
453 
454  protected:
457  std::size_t steps_;
458  Population population_;
459 };
460 
461 } // namespace simplega
462 } // namespace mc2lib
463 
464 #endif /* SIMPLEGA_HPP_ */
465 
466 /* vim: set ts=2 sts=2 sw=2 et : */
Helper to manages and evolve a populates.
Definition: simplega.hpp:226
Selection SelectRoulette(URNG &urng, std::size_t count)
Definition: simplega.hpp:364
Definition: cats.hpp:47
Container genome_
Raw genome of individual genes of T.
Definition: simplega.hpp:216
void SelectionSort(Selection *v)
Definition: simplega.hpp:383
std::vector< T > Container
Definition: simplega.hpp:131
std::size_t target_population_size_
Definition: simplega.hpp:455
Simple Genome interface.
Definition: simplega.hpp:129
void set_mutation_rate(float mutation_rate)
Definition: simplega.hpp:286
Container * get_ptr()
Modifiable genome accessor.
Definition: simplega.hpp:161
std::list< GenomeT > Population
Definition: simplega.hpp:232
Selection SelectAll()
Definition: simplega.hpp:324
GenePool(std::size_t target_population_size=80, float mutation_rate=0.02f)
Definition: simplega.hpp:235
virtual ~Genome()
Definition: simplega.hpp:147
Population population_
Definition: simplega.hpp:458
float mutation_rate() const
Definition: simplega.hpp:284
virtual bool operator<(const Genome &rhs) const
Less than comparison operator.
Definition: simplega.hpp:171
float mutation_rate_
Definition: simplega.hpp:456
void Step(URNG &urng, CrossoverMutateFunc crossover_mutate, const Selection &selection, std::size_t mates, std::size_t keep=0, std::size_t mate1_stride=1, std::size_t mate2_stride=1)
Definition: simplega.hpp:397
Selection SelectUniform(URNG &urng, std::size_t count)
Definition: simplega.hpp:375
std::size_t target_population_size() const
Definition: simplega.hpp:290
std::size_t steps() const
Definition: simplega.hpp:294
void CutSpliceMutate(URNG &urng, const GenomeT &mate1, const GenomeT &mate2, float mutation_rate, C *container)
Definition: simplega.hpp:71
float BestFitness() const
Definition: simplega.hpp:308
std::vector< GenomeT * > Selection
Definition: simplega.hpp:233
Genome(Container g)
Converting constructor.
Definition: simplega.hpp:145
Genome()
Default constructor.
Definition: simplega.hpp:138
GenePool(Selection selection, float mutation_rate=0.02f)
Definition: simplega.hpp:257
GenePool(Population population, float mutation_rate=0.02f)
Definition: simplega.hpp:251
void Sort()
Definition: simplega.hpp:315
std::size_t steps_
Definition: simplega.hpp:457
Population * get_ptr()
Definition: simplega.hpp:282
Selection SelectDist(URNG &urng, DIST &dist, std::size_t count)
Definition: simplega.hpp:337
virtual float Fitness() const =0
Fitness accessor.
float WorstFitness() const
Definition: simplega.hpp:304
const GenomeT & SelectBest() const
Definition: simplega.hpp:317
std::size_t population_size() const
Definition: simplega.hpp:292
float AverageFitness() const
Definition: simplega.hpp:296