3 * \remark This file is part of VITA.
5 * \copyright Copyright (C) 2013-2023 EOS di Manlio Morini.
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/
13#if !defined(VITA_EVOLUTION_SELECTION_H)
14# error "Don't include this file directly, include the specific .h instead"
17#if !defined(VITA_EVOLUTION_SELECTION_TCC)
18#define VITA_EVOLUTION_SELECTION_TCC
21/// \param[in] pop current population
22/// \param[in] eva current evaluator
23/// \param[in] sum up to date summary of the evolution
26strategy<T>::strategy(const population<T> &pop, evaluator<T> &eva,
27 const summary<T> &sum)
28 : pop_(pop), eva_(eva), sum_(sum)
33/// \return a collection of coordinates of individuals ordered in descending
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
39/// Recall that better individuals have higher fitness.
41/// Parameters from the environment:
42/// * `mate_zone` - to restrict the selection of individuals to a segment of
44/// * `tournament_size` - to control selection pressure.
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).
56typename strategy<T>::parents_t tournament<T>::run()
58 const auto &pop(this->pop_);
60 const auto rounds(pop.get_problem().env.tournament_size);
63 const auto target(pickup(pop));
64 typename strategy<T>::parents_t ret(rounds);
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)
71 const auto new_coord(pickup(pop, target));
72 const auto new_fitness(this->eva_(pop[new_coord]));
76 for (; j && new_fitness > this->eva_(pop[ret[j - 1]]); --j)
83 assert(ret.size() == rounds);
85 for (unsigned i(1); i < rounds; ++i)
86 assert(this->eva_(pop[ret[i - 1]]) >= this->eva_(pop[ret[i]]));
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
97/// \return the coordinates of a random individual in layer `l` or `l-1`
100typename population<T>::coord alps<T>::pickup(unsigned l, double p) const
102 Expects(l < this->pop_.layers());
103 Expects(0.0 <= p && p <= 1.0);
105 if (l > 0 && !vita::random::boolean(p))
108 return {l, vita::random::sup(this->pop_.individuals(l))};
112bool alps<T>::aged(const typename population<T>::coord &c) const
114 const auto &pop(this->pop_);
116 return pop.get_problem().env.alps.aged(pop[c], c.layer, pop.layers());
120/// \return a vector of coordinates of chosen individuals
122/// Parameters from the environment:
123/// - `mate_zone` to restrict the selection of individuals to a segment of
125/// - `tournament_size` to control number of selected individuals.
128typename strategy<T>::parents_t alps<T>::run()
130 const auto &pop(this->pop_);
132 const auto layer(vita::random::sup(pop.layers()));
134 auto c0(this->pickup(layer));
135 auto c1(this->pickup(layer));
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])};
143 if (age_fit0 < age_fit1)
146 std::swap(age_fit0, age_fit1);
149 assert(age_fit0 >= age_fit1);
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);
157 const auto tmp(this->pickup(layer, same_layer_p));
158 const age_fit_t tmp_age_fit{!aged(tmp), this->eva_(pop[tmp])};
160 if (age_fit0 < tmp_age_fit)
166 age_fit0 = tmp_age_fit;
168 else if (age_fit1 < tmp_age_fit)
171 age_fit1 = tmp_age_fit;
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);
190/// \return a vector of coordinates of individuals partially ordered with the
191/// pareto-dominance criterion.
193/// Parameters from the environment:
194/// * tournament_size - to control selection pressure (the number of randomly
195/// selected individuals for dominance evaluation).
198typename strategy<T>::parents_t pareto<T>::run()
200 const auto &pop(this->pop_);
201 const auto rounds(pop.env().tournament_size);
203 std::vector<unsigned> pool(rounds);
204 for (unsigned i(0); i < rounds; ++i)
205 pool.push_back(pickup(pop));
209 std::set<unsigned> front_set, dominated_set;
210 this->front(pool, &front_set, &dominated_set);
212 assert(front_set.size());
214 typename strategy<T>::parents_t ret{
215 {0, vita::random::element(front_set)},
216 {0, vita::random::element(front_set)}};
218 if (dominated_set.size())
219 ret.push_back({0, vita::random::element(dominated_set)});
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)
230void pareto<T>::front(const std::vector<unsigned> &pool,
231 std::set<unsigned> *fs, std::set<unsigned> *ds) const
233 const auto &pop(this->pop_);
235 for (const auto &ind : pool)
237 if (fs->find(ind) != fs->end() || ds->find(ind) != ds->end())
240 const auto ind_fit(this->eva_(pop[{0, ind}]));
242 bool ind_dominated(false);
243 for (auto f(fs->cbegin()); f != fs->cend() && !ind_dominated;)
244 // no increment in the for loop
246 const auto f_fit(this->eva_(pop[{0, *f}]));
248 if (!ind_dominated && ind_fit.dominating(f_fit))
255 if (f_fit.dominating(ind_fit))
257 ind_dominated = true;
271/// \return a vector of coordinates of randomly chosen individuals.
273/// Parameters from the environment:
274/// * tournament_size - to control number of selected individuals.
277typename strategy<T>::parents_t random<T>::run()
279 const auto pop(this->pop_);
280 const auto size(pop.get_problem().env.tournament_size);
283 typename strategy<T>::parents_t ret(size);
290#endif // include guard