Vita
evolution_selection.tcc
1/**
2 * \file
3 * \remark This file is part of VITA.
4 *
5 * \copyright Copyright (C) 2013-2023 EOS di Manlio Morini.
6 *
7 * \license
8 * This Source Code Form is subject to the terms of the Mozilla Public
9 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
10 * You can obtain one at http://mozilla.org/MPL/2.0/
11 */
12
13#if !defined(VITA_EVOLUTION_SELECTION_H)
14# error "Don't include this file directly, include the specific .h instead"
15#endif
16
17#if !defined(VITA_EVOLUTION_SELECTION_TCC)
18#define VITA_EVOLUTION_SELECTION_TCC
19
20///
21/// \param[in] pop current population
22/// \param[in] eva current evaluator
23/// \param[in] sum up to date summary of the evolution
24///
25template<class T>
26strategy<T>::strategy(const population<T> &pop, evaluator<T> &eva,
27 const summary<T> &sum)
28 : pop_(pop), eva_(eva), sum_(sum)
29{
30}
31
32///
33/// \return a collection of coordinates of individuals ordered in descending
34/// fitness
35///
36/// Tournament selection works by selecting a number of individuals from the
37/// population at random (a tournament) and then choosing only the best of
38/// those individuals.
39/// Recall that better individuals have higher fitness.
40///
41/// Parameters from the environment:
42/// * `mate_zone` - to restrict the selection of individuals to a segment of
43/// the population;
44/// * `tournament_size` - to control selection pressure.
45///
46/// \remark
47/// Different compilers may optimize the code producing slightly different
48/// sortings (due to floating point approximations). This is a known *issue*.
49/// Anyway we keep using the `<` operator because:
50/// - it's faster than the `std::fabs(delta)` approach;
51/// - the additional *noise* is marginal (for the GAs/GP standard);
52/// - for debugging purposes *compiler-stability* is enough (and we have faith
53/// in the test suite).
54///
55template<class T>
56typename strategy<T>::parents_t tournament<T>::run()
57{
58 const auto &pop(this->pop_);
59
60 const auto rounds(pop.get_problem().env.tournament_size);
61 assert(rounds);
62
63 const auto target(pickup(pop));
64 typename strategy<T>::parents_t ret(rounds);
65
66 // This is the inner loop of an insertion sort algorithm. It's simple, fast
67 // (if `rounds` is small) and doesn't perform too much comparisons.
68 // DO NOT USE `std::sort` it's way slower.
69 for (unsigned i(0); i < rounds; ++i)
70 {
71 const auto new_coord(pickup(pop, target));
72 const auto new_fitness(this->eva_(pop[new_coord]));
73
74 auto j(i);
75
76 for (; j && new_fitness > this->eva_(pop[ret[j - 1]]); --j)
77 ret[j] = ret[j - 1];
78
79 ret[j] = new_coord;
80 }
81
82#if !defined(NDEBUG)
83 assert(ret.size() == rounds);
84
85 for (unsigned i(1); i < rounds; ++i)
86 assert(this->eva_(pop[ret[i - 1]]) >= this->eva_(pop[ret[i]]));
87#endif
88
89 return ret;
90}
91
92///
93/// \param[in] l a layer
94/// \param[in] p the probability of extracting an individual in layer `l`
95/// (`1 - p` is the probability of extracting an individual
96/// in layer `l-1`)
97/// \return the coordinates of a random individual in layer `l` or `l-1`
98///
99template<class T>
100typename population<T>::coord alps<T>::pickup(unsigned l, double p) const
101{
102 Expects(l < this->pop_.layers());
103 Expects(0.0 <= p && p <= 1.0);
104
105 if (l > 0 && !vita::random::boolean(p))
106 --l;
107
108 return {l, vita::random::sup(this->pop_.individuals(l))};
109}
110
111template<class T>
112bool alps<T>::aged(const typename population<T>::coord &c) const
113{
114 const auto &pop(this->pop_);
115
116 return pop.get_problem().env.alps.aged(pop[c], c.layer, pop.layers());
117}
118
119///
120/// \return a vector of coordinates of chosen individuals
121///
122/// Parameters from the environment:
123/// - `mate_zone` to restrict the selection of individuals to a segment of
124/// the population;
125/// - `tournament_size` to control number of selected individuals.
126///
127template<class T>
128typename strategy<T>::parents_t alps<T>::run()
129{
130 const auto &pop(this->pop_);
131
132 const auto layer(vita::random::sup(pop.layers()));
133
134 auto c0(this->pickup(layer));
135 auto c1(this->pickup(layer));
136
137 // This type is used to take advantage of the lexicographic comparison
138 // capabilities of std::pair.
139 using age_fit_t = std::pair<bool, fitness_t>;
140 age_fit_t age_fit0{!aged(c0), this->eva_(pop[c0])};
141 age_fit_t age_fit1{!aged(c1), this->eva_(pop[c1])};
142
143 if (age_fit0 < age_fit1)
144 {
145 std::swap(c0, c1);
146 std::swap(age_fit0, age_fit1);
147 }
148
149 assert(age_fit0 >= age_fit1);
150
151 const auto &env(pop.get_problem().env);
152 const auto same_layer_p(env.alps.p_same_layer);
153 auto rounds(env.tournament_size);
154
155 while (rounds--)
156 {
157 const auto tmp(this->pickup(layer, same_layer_p));
158 const age_fit_t tmp_age_fit{!aged(tmp), this->eva_(pop[tmp])};
159
160 if (age_fit0 < tmp_age_fit)
161 {
162 c1 = c0;
163 age_fit1 = age_fit0;
164
165 c0 = tmp;
166 age_fit0 = tmp_age_fit;
167 }
168 else if (age_fit1 < tmp_age_fit)
169 {
170 c1 = tmp;
171 age_fit1 = tmp_age_fit;
172 }
173
174 assert(age_fit0.first == !aged(c0));
175 assert(age_fit1.first == !aged(c1));
176 assert(age_fit0.second == this->eva_(pop[c0]));
177 assert(age_fit1.second == this->eva_(pop[c1]));
178 assert(age_fit0 >= age_fit1);
179 assert(!aged(c0) || aged(c1));
180 assert(c0.layer <= layer);
181 assert(layer <= c0.layer + 1);
182 assert(c1.layer <= layer);
183 assert(layer <= c1.layer + 1);
184 }
185
186 return {c0, c1};
187}
188
189///
190/// \return a vector of coordinates of individuals partially ordered with the
191/// pareto-dominance criterion.
192///
193/// Parameters from the environment:
194/// * tournament_size - to control selection pressure (the number of randomly
195/// selected individuals for dominance evaluation).
196///
197template<class T>
198typename strategy<T>::parents_t pareto<T>::run()
199{
200 const auto &pop(this->pop_);
201 const auto rounds(pop.env().tournament_size);
202
203 std::vector<unsigned> pool(rounds);
204 for (unsigned i(0); i < rounds; ++i)
205 pool.push_back(pickup(pop));
206
207 assert(pool.size());
208
209 std::set<unsigned> front_set, dominated_set;
210 this->front(pool, &front_set, &dominated_set);
211
212 assert(front_set.size());
213
214 typename strategy<T>::parents_t ret{
215 {0, vita::random::element(front_set)},
216 {0, vita::random::element(front_set)}};
217
218 if (dominated_set.size())
219 ret.push_back({0, vita::random::element(dominated_set)});
220
221 return ret;
222}
223
224///
225/// \param[in] pool indexes of individuals in the population
226/// \param[out] fs the set of nondominated individuals of `pool` (front set)
227/// \param[out] ds the set of dominated individuals of `pool (dominated set)
228///
229template<class T>
230void pareto<T>::front(const std::vector<unsigned> &pool,
231 std::set<unsigned> *fs, std::set<unsigned> *ds) const
232{
233 const auto &pop(this->pop_);
234
235 for (const auto &ind : pool)
236 {
237 if (fs->find(ind) != fs->end() || ds->find(ind) != ds->end())
238 continue;
239
240 const auto ind_fit(this->eva_(pop[{0, ind}]));
241
242 bool ind_dominated(false);
243 for (auto f(fs->cbegin()); f != fs->cend() && !ind_dominated;)
244 // no increment in the for loop
245 {
246 const auto f_fit(this->eva_(pop[{0, *f}]));
247
248 if (!ind_dominated && ind_fit.dominating(f_fit))
249 {
250 ds->insert(*f);
251 f = fs->erase(f);
252 }
253 else
254 {
255 if (f_fit.dominating(ind_fit))
256 {
257 ind_dominated = true;
258 ds->insert(ind);
259 }
260
261 ++f;
262 }
263 }
264
265 if (!ind_dominated)
266 fs->insert(ind);
267 }
268}
269
270///
271/// \return a vector of coordinates of randomly chosen individuals.
272///
273/// Parameters from the environment:
274/// * tournament_size - to control number of selected individuals.
275///
276template<class T>
277typename strategy<T>::parents_t random<T>::run()
278{
279 const auto pop(this->pop_);
280 const auto size(pop.get_problem().env.tournament_size);
281
282 assert(size);
283 typename strategy<T>::parents_t ret(size);
284
285 for (auto &v : ret)
286 v = pickup(pop);
287
288 return ret;
289}
290#endif // include guard