Grail (C++)  1.4.0
A multi-platform, modular, universal engine for embedding advanced AI in games.
RouletteSampler.hh
1 // Copyright QED Software 2023.
2 
3 #ifndef GRAIL_ROULETTE_SAMPLER_H
4 #define GRAIL_ROULETTE_SAMPLER_H
5 
6 #include <memory>
7 #include <numeric>
8 #include <random>
9 
10 namespace grail
11 {
12 namespace evolution
13 {
20  template <class T>
22  {
23  public:
29 
34  RouletteSampler(const std::vector<std::unique_ptr<T>>& fitnessHavingElements);
35 
40  void Initialize(const std::vector<std::unique_ptr<T>>& fitnessHavingElements);
41 
47  T* Sample(std::mt19937_64& randGen);
48 
54  T* SampleWithReturn(std::mt19937_64& randGen);
55 
62  std::vector<T*> SampleMany(size_t count, std::mt19937_64& randGen);
63 
70  std::vector<T*> SampleManyWithReturn(size_t count, std::mt19937_64& randGen);
71 
78  std::vector<size_t> SampleManyIndices(size_t count, std::mt19937_64& randGen);
79 
80  private:
86  std::size_t SampleIndex(std::mt19937_64& randGen);
87 
92  std::size_t SampleIndexWithReturn(std::mt19937_64& randGen) const;
93 
94  std::size_t RemoveIndex(std::size_t index);
95 
96  std::vector<T*> data{};
97  double totalFitness = 0.0;
98  };
99 
100  template <class T>
101  inline std::size_t RouletteSampler<T>::RemoveIndex(std::size_t index)
102  {
103  totalFitness -= data[index]->GetFitness();
104  data.erase(data.begin() + index);
105  return index;
106  }
107 
108  template <class T>
109  inline void RouletteSampler<T>::Initialize(const std::vector<std::unique_ptr<T>>& fitnessHavingElements)
110  {
111  data.clear();
112  totalFitness = 0.0;
113  for(auto& individual : fitnessHavingElements)
114  {
115  totalFitness += individual->GetFitness();
116  data.push_back(individual.get());
117  }
118  }
119 
120  template <class T>
122  : totalFitness{0.0}
123  {
124  }
125 
126  template <class T>
127  inline RouletteSampler<T>::RouletteSampler(const std::vector<std::unique_ptr<T>>& fitnessHavingElements)
128  {
129  Initialize(fitnessHavingElements);
130  }
131 
132  template <class T>
133  inline std::size_t RouletteSampler<T>::SampleIndex(std::mt19937_64& randGen)
134  {
135  return RemoveIndex(SampleIndexWithReturn(randGen));
136  }
137 
138  template <class T>
139  inline std::size_t RouletteSampler<T>::SampleIndexWithReturn(std::mt19937_64& randGen) const
140  {
141  if(totalFitness <= std::numeric_limits<double>::epsilon())
142  {
143  return std::uniform_int_distribution<size_t>(0, data.size())(randGen);
144  }
145 
146  double val = std::uniform_real_distribution<double>(0, totalFitness)(randGen);
147  for(size_t i = 0; i < data.size(); ++i)
148  {
149  if(val < data[i]->GetFitness())
150  {
151  return i;
152  }
153 
154  val -= data[i]->GetFitness();
155  }
156  return data.size() - 1;
157  }
158 
159  template <class T>
160  inline std::vector<size_t> RouletteSampler<T>::SampleManyIndices(size_t count, std::mt19937_64& randGen)
161  {
162  std::vector<size_t> retData;
163  if(count >= data.size())
164  {
165  retData.resize(data.size());
166  std::iota(retData.begin(), retData.end(), 0);
167  }
168  else
169  {
170  for(size_t i = 0; i < count; ++i)
171  {
172  retData.push_back(SampleIndex(randGen));
173  }
174  }
175  return retData;
176  }
177 
178  template <class T>
179  inline T* RouletteSampler<T>::Sample(std::mt19937_64& randGen)
180  {
181  if(data.size() == 0)
182  {
183  return nullptr;
184  }
185 
186  std::size_t index = SampleIndexWithReturn(randGen);
187  T* individual = data[index];
188  RemoveIndex(index);
189  return individual;
190  }
191 
192  template <class T>
193  inline T* RouletteSampler<T>::SampleWithReturn(std::mt19937_64& randGen)
194  {
195  if (data.size() == 0)
196  {
197  return nullptr;
198  }
199 
200  return data[SampleIndexWithReturn(randGen)];
201  }
202 
203  template <class T>
204  inline std::vector<T*> RouletteSampler<T>::SampleMany(std::size_t count, std::mt19937_64& randGen)
205  {
206  if(count >= data.size())
207  {
208  return std::vector<T*>(data);
209  }
210 
211  std::vector<T*> retData;
212  for(std::size_t i = 0; i < count; ++i)
213  {
214  retData.push_back(Sample(randGen));
215  }
216  return retData;
217  }
218 
219  template <class T>
220  inline std::vector<T*> RouletteSampler<T>::SampleManyWithReturn(std::size_t count, std::mt19937_64& randGen)
221  {
222  if (count >= data.size())
223  {
224  return std::vector<T*>(data);
225  }
226 
227  std::vector<T*> retData;
228  for (std::size_t i = 0; i < count; ++i)
229  {
230  retData.push_back(SampleWithReturn(randGen));
231  }
232  return retData;
233  }
234 }
235 }
236 
237 #endif // GRAIL_ROULETTE_SAMPLER_H
grail::evolution::RouletteSampler
Provides a functionality of a fitness proportional roulette sampling of any objects that implement a ...
Definition: RouletteSampler.hh:21
grail::evolution::RouletteSampler::RouletteSampler
RouletteSampler()
RouletteSampler - Constructor. Note that if you use a parameterless constructor, call Initialize() to...
Definition: RouletteSampler.hh:121
grail::evolution::RouletteSampler::SampleWithReturn
T * SampleWithReturn(std::mt19937_64 &randGen)
Samples one element without removing it.
Definition: RouletteSampler.hh:193
grail::evolution::RouletteSampler::Sample
T * Sample(std::mt19937_64 &randGen)
Samples one element and removes it.
Definition: RouletteSampler.hh:179
grail::evolution::RouletteSampler::SampleMany
std::vector< T * > SampleMany(size_t count, std::mt19937_64 &randGen)
Samples a given number of elements and removes them.
Definition: RouletteSampler.hh:204
grail::evolution::RouletteSampler::Initialize
void Initialize(const std::vector< std::unique_ptr< T >> &fitnessHavingElements)
Sets the elements that can be sampled using this RouletteSampler. They must implement a public "doubl...
Definition: RouletteSampler.hh:109
grail::evolution::RouletteSampler::SampleManyWithReturn
std::vector< T * > SampleManyWithReturn(size_t count, std::mt19937_64 &randGen)
Samples a given number of elements without removing them.
Definition: RouletteSampler.hh:220
grail::evolution::RouletteSampler::SampleManyIndices
std::vector< size_t > SampleManyIndices(size_t count, std::mt19937_64 &randGen)
SampleManyIndices - samples a given number of indices of elements and removes them.
Definition: RouletteSampler.hh:160