Vita
population.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_POPULATION_H)
14# error "Don't include this file directly, include the specific .h instead"
15#endif
16
17#if !defined(VITA_POPULATION_TCC)
18#define VITA_POPULATION_TCC
19
20///
21/// Creates a random population.
22///
23/// \param[in] p current problem
24///
25template<class T>
26population<T>::population(const problem &p) : prob_(&p), pop_(1),
27 allowed_(1)
28{
29 const auto n(p.env.individuals);
30 pop_[0].reserve(n);
31 allowed_[0] = n;
32
33 init_layer(0);
34
35 Ensures(is_valid());
36}
37
38///
39/// Resets layer `l` of the population.
40///
41/// \param[in] l a layer of the population
42///
43/// \warning If layer `l` is nonexistent/empty the method doesn't work!
44///
45template<class T>
46void population<T>::init_layer(unsigned l)
47{
48 Expects(l < layers());
49
50 pop_[l].clear();
51
52 std::generate_n(std::back_inserter(pop_[l]), allowed(l),
53 [this] {return T(get_problem()); });
54}
55
56///
57/// \return number of active layers
58///
59/// \note
60/// The number of active layers is a dynamic value (almost monotonically
61/// increasing with the generation number).
62///
63template<class T>
64unsigned population<T>::layers() const
65{
66 return static_cast<unsigned>(pop_.size());
67}
68
69///
70/// Adds a new layer to the population.
71///
72/// The new layer is inserted as the lower layer and randomly initialized.
73///
74template<class T>
75void population<T>::add_layer()
76{
77 Expects(layers());
78 Expects(individuals(0));
79
80 const auto individuals(get_problem().env.individuals);
81
82 pop_.insert(pop_.begin(), layer_t());
83 pop_[0].reserve(individuals);
84
85 allowed_.insert(allowed_.begin(), individuals);
86
87 init_layer(0);
88}
89
90template<class T>
91void population<T>::remove_layer(unsigned l)
92{
93 Expects(l < layers());
94
95 pop_.erase(std::next(pop_.begin(), l));
96 allowed_.erase(std::next(allowed_.begin(), l));
97}
98
99///
100/// Adds individual `i` to layer `l`.
101///
102/// \param[in] l index of a layer
103/// \param[in] i an individual
104///
105template<class T>
106void population<T>::add_to_layer(unsigned l, const T &i)
107{
108 Expects(l < layers());
109
110 if (individuals(l) < allowed(l))
111 pop_[l].push_back(i);
112}
113
114///
115/// Removes the last individual of layer `l`.
116///
117/// \param[in] l index of a layer
118///
119template<class T>
120void population<T>::pop_from_layer(unsigned l)
121{
122 Expects(l < layers());
123 pop_[l].pop_back();
124}
125
126///
127/// \param[in] c coordinates of an individual
128/// \return a reference to the individual at coordinates `c`
129///
130template<class T>
131T &population<T>::operator[](coord c)
132{
133 Expects(c.layer < layers());
134 Expects(c.index < individuals(c.layer));
135 return pop_[c.layer][c.index];
136}
137
138///
139/// \param[in] c coordinates of an individual
140/// \return a constant reference to the individual at coordinates `c`
141///
142template<class T>
143const T &population<T>::operator[](coord c) const
144{
145 Expects(c.layer < layers());
146 Expects(c.index < individuals(c.layer));
147 return pop_[c.layer][c.index];
148}
149
150///
151/// \param[in] l a layer
152/// \return the number of individuals allowed in layer `l`
153///
154/// \note `for each l individuals(l) < allowed(l)`
155///
156template<class T>
157unsigned population<T>::allowed(unsigned l) const
158{
159 Expects(l < layers());
160 return allowed_[l];
161}
162
163///
164/// Sets the number of programs allowed in layer `l`.
165///
166/// \param[in] l a layer
167/// \param[in] n number of programs allowed in layer `l`
168///
169/// If layer `l` contains more programs than the amount allowed, the surplus
170/// is deleted.
171///
172template<class T>
173void population<T>::set_allowed(unsigned l, unsigned n)
174{
175 Expects(l < layers());
176 Expects(n <= pop_[l].capacity());
177
178 const auto &env(get_problem().env);
179 // Unless explicitly allowed by the environment, do not drop under a
180 // predefined number of individuals.
181 const auto min(std::min(env.min_individuals, env.individuals));
182 n = std::max(n, min);
183
184 if (individuals(l) > n)
185 {
186 const auto delta(individuals(l) - n);
187
188 // We should consider the remove-erase idiom for deleting delta random
189 // elements.
190 if (delta)
191 pop_[l].erase(pop_[l].end() - delta, pop_[l].end());
192 }
193
194 allowed_[l] = n;
195
196 Ensures(is_valid());
197}
198
199///
200/// \param[in] l a layer
201/// \return the number of individuals in layer `l`
202///
203template<class T>
204unsigned population<T>::individuals(unsigned l) const
205{
206 Expects(l < layers());
207 return static_cast<unsigned>(pop_[l].size());
208}
209
210///
211/// \return the number of individuals in the population
212///
213template<class T>
214unsigned population<T>::individuals() const
215{
216 using ret_t = decltype(individuals());
217
218 return std::accumulate(pop_.begin(), pop_.end(), ret_t(0),
219 [](auto accumulator, const auto &layer)
220 {
221 return accumulator
222 + static_cast<ret_t>(layer.size());
223 });
224}
225
226///
227/// \return a constant reference to the active problem
228///
229template<class T>
230const problem &population<T>::get_problem() const
231{
232 Expects(prob_);
233 return *prob_;
234}
235
236///
237/// \return a `const_iterator` pointing to the first individual of the
238/// population
239///
240template<class T>
241typename population<T>::const_iterator population<T>::begin() const
242{
243 return const_iterator(*this, true);
244}
245
246///
247/// \return an iterator pointing one element past the last individual of the
248/// population
249///
250template<class T>
251typename population<T>::const_iterator population<T>::end() const
252{
253 return const_iterator(*this, false);
254}
255
256///
257/// Increments the age of each individual of the population
258///
259template<class T>
260void population<T>::inc_age()
261{
262 for (auto &l : pop_)
263 for (auto &i : l)
264 i.inc_age();
265}
266
267///
268/// \return `true` if the object passes the internal consistency check
269///
270template<class T>
271bool population<T>::is_valid() const
272{
273 for (const auto &l : pop_)
274 for (const auto &i : l)
275 if (!i.is_valid())
276 return false;
277
278 if (layers() != allowed_.size())
279 {
280 vitaERROR << "Number of layers doesn't match allowed array size";
281 return false;
282 }
283
284 const auto n(layers());
285 for (auto l(decltype(n){0}); l < n; ++l)
286 {
287 if (allowed(l) < individuals(l))
288 return false;
289
290 if (pop_[l].capacity() < allowed(l))
291 return false;
292 }
293
294 if (!prob_)
295 {
296 vitaERROR << "Undefined `problem`";
297 return false;
298 }
299
300 return true;
301}
302
303///
304/// \param[in] prob current problem
305/// \param[in] in input stream
306/// \return `true` if population was loaded correctly
307///
308/// \note The current population isn't changed if the load operation fails.
309///
310template<class T>
311bool population<T>::load(std::istream &in, const problem &prob)
312{
313 unsigned n_layers;
314 if (!(in >> n_layers) || !n_layers)
315 return false;
316
317 population p(prob);
318 p.pop_.reserve(n_layers);
319 p.allowed_.reserve(n_layers);
320
321 for (decltype(n_layers) l(0); l < n_layers; ++l)
322 {
323 if (!(in >> p.allowed_[l]))
324 return false;
325
326 unsigned n_elem(0);
327 if (!(in >> n_elem))
328 return false;
329
330 for (decltype(n_elem) i(0); i < n_elem; ++i)
331 if (!p[{l, i}].load(in, prob.sset))
332 return false;
333 }
334
335 *this = p;
336 return true;
337}
338
339///
340/// \param[out] out output stream
341/// \return `true` if population was saved correctly
342///
343template<class T>
344bool population<T>::save(std::ostream &out) const
345{
346 const auto n(layers());
347
348 out << n << '\n';
349
350 for (auto l(decltype(n){0}); l < n; ++l)
351 {
352 out << allowed(l) << ' ' << individuals(l) << '\n';
353
354 for (const auto &prg : pop_[l])
355 prg.save(out);
356 }
357
358 return out.good();
359}
360
361///
362/// \param[in] p a population
363/// \return the index of a random individual in `p`
364///
365/// \related population
366///
367template<class T>
368typename population<T>::coord pickup(const population<T> &p)
369{
370 const auto n_layers(p.layers());
371
372 if (n_layers == 1)
373 return {0, random::sup(p.individuals(0))};
374
375 // With multiple layers we cannot be sure that every layer has the same
376 // number of individuals. So the simple (and fast) solution:
377 //
378 // const auto l(random::sup(n_layers));
379 // return return {l, random::sup(p.individuals(l)};
380 //
381 // isn't appropriate.
382
383 std::vector<unsigned> s(n_layers);
384 std::generate(s.begin(), s.end(),
385 [&p, l = 0] () mutable { return p.individuals(l++); });
386
387 std::discrete_distribution<unsigned> dd(s.begin(), s.end());
388 const auto l(dd(random::engine));
389 return {l, random::sup(s[l])};
390}
391
392///
393/// \param[in] p a population
394/// \param[in] target coordinates of a reference individual
395/// \return the coordinates of a random individual *near* `target`
396///
397/// \related population
398///
399/// Other parameters from the environment:
400/// * mate_zone - restricts the selection of individuals to a segment of the
401/// population.
402///
403template<class T>
404typename population<T>::coord pickup(const population<T> &p,
405 typename population<T>::coord target)
406{
407 const auto mate_zone(p.get_problem().env.mate_zone);
408 const auto individuals(p.individuals(target.layer));
409
410 return {target.layer, random::ring(target.index, mate_zone, individuals)};
411}
412
413///
414/// \param[in,out] s output stream
415/// \param[in] pop population to be listed
416/// \return the output stream
417///
418template<class T>
419std::ostream &operator<<(std::ostream &s, const population<T> &pop)
420{
421 unsigned n_layer(0);
422
423 for (const auto &l : pop)
424 {
425 s << std::string(70, '-') << "\nLayer " << n_layer
426 << std::string(70, '-') << '\n';
427
428 for (const auto &i : l)
429 s << i << '\n';
430
431 ++n_layer;
432 }
433
434 return s;
435}
436#endif // include guard