Quetzal-CoaTL
The Coalescence Template Library
Loading...
Searching...
No Matches
demographic_policy.hpp
1// Copyright 2021 Arnaud Becheler <abechele@umich.edu>
2
3/*********************************************************************** * This program is free software; you can
4 *redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free
5 *Software Foundation; either version 2 of the License, or * (at your option) any later version. *
6 * *
7 ***************************************************************************/
8
9#pragma once
10
11#include "../utils/PointWithId.hpp"
12#include "../utils/matrix_operation.hpp"
13
14#include <boost/numeric/ublas/matrix_sparse.hpp>
15#include <boost/numeric/ublas/symmetric.hpp>
16
17#include <memory> // make_shared
18#include <numeric> // std::transform std::accumulate
19#include <random> // std::discrete_distribution
20#include <vector>
21
22namespace quetzal
23{
24namespace demography
25{
26namespace demographic_policy
27{
40{
41 private:
50 template <typename Space, typename Matrix> class Implementation
51 {
52 private:
53 using coord_type = Space;
54 // distance matrix type, can be sparse or symmetric
55 using matrix_type = Matrix;
56 // for easier access to matrix row/columns ID
57 using point_ID_type = quetzal::utils::PointWithId<coord_type>;
58 // owe the matrix, heavy object
59 matrix_type m_matrix;
60 // don't owe the vector
61 std::vector<point_ID_type> const &m_points;
62 // don't owe the space
63 std::vector<coord_type> const &m_coords;
64 // specific to individual-based: stores discrete distributions centered on departure
65 using distribution_type = std::discrete_distribution<size_t>;
66 mutable std::vector<distribution_type> m_distributions;
78 template <typename F> matrix_type make_weight_matrix(std::vector<coord_type> const &coords, F w)
79 {
80 matrix_type A(coords.size(), coords.size());
81 for (unsigned i = 0; i < A.size1(); ++i)
82 {
83 auto x = coords.at(i);
84 for (unsigned j = 0; j < A.size2(); ++j)
85 {
86 auto y = coords.at(j);
87 A(i, j) = w(x, y);
88 }
89 }
90 return A;
91 }
97 std::vector<distribution_type> make_distributions() const
98 {
99 std::vector<distribution_type> dists;
100 dists.reserve(m_matrix.size1());
101 for (unsigned i = 0; i < m_matrix.size1(); ++i)
102 {
103 std::vector<double> weights;
104 weights.reserve(m_matrix.size2());
105 for (unsigned j = 0; j < m_matrix.size2(); ++j)
106 {
107 weights.push_back(m_matrix(i, j));
108 }
109 dists.emplace_back(weights.cbegin(), weights.cend());
110 }
111 return dists;
112 }
113
114 public:
118 Implementation(const Implementation &) = delete;
124 template <typename F>
125 Implementation(std::vector<point_ID_type> const &points, std::vector<coord_type> const &coords, F f)
126 : m_matrix(make_weight_matrix(coords, f)), m_points(points), m_coords(coords),
127 m_distributions(make_distributions())
128 {
129 }
133 template <typename Generator> coord_type sample_arrival(Generator &gen, coord_type const &x) const
134 {
135 return m_coords[m_distributions[quetzal::utils::getIndexOfPointInVector(x, m_coords)].operator()(gen)];
136 }
137 }; // end inner class Implementation
138
142 template <typename Space, typename Matrix> class Interface
143 {
144 private:
145 using impl_type = Implementation<Space, Matrix>;
146 using coord_type = Space;
147 using point_ID_type = quetzal::utils::PointWithId<coord_type>;
148 // makes copy of the interface is ok
149 std::shared_ptr<impl_type> m_pimpl;
150
151 public:
155 template <typename F>
156 Interface(std::vector<point_ID_type> const &points, std::vector<coord_type> const &coords, F f)
157 : m_pimpl(std::make_shared<impl_type>(points, coords, f))
158 {
159 }
167 template <typename Generator> coord_type operator()(Generator &gen, coord_type const &x)
168 {
169 return m_pimpl->sample_arrival(gen, x);
170 }
171 }; // end inner class Interface
172
173 public:
175 using value_type = unsigned int;
184 template <typename T, typename F> static auto make_distance_based_dispersal(std::vector<T> const &coords, F f)
185 {
186 using coord_type = T;
187 using point_ID_type = quetzal::utils::PointWithId<coord_type>;
188 std::vector<point_ID_type> points;
189 points.reserve(coords.size());
190 const auto &ref = coords;
191 std::transform(coords.begin(), coords.end(), std::back_inserter(points),
192 [ref](coord_type const &x) { return point_ID_type(ref, x); });
193 using matrix_type = boost::numeric::ublas::symmetric_matrix<double>;
194 return Interface<coord_type, matrix_type>(points, coords, f);
195 }
196}; // end individual_based
197
207{
208 private:
214 template <typename Space, typename Matrix> class Implementation
215 {
216 private:
217 using coord_type = Space;
218 // distance matrix type, can be sparse or symmetric
219 using matrix_type = Matrix;
220 // for easier access to matrix row/columns ID
221 using point_ID_type = quetzal::utils::PointWithId<coord_type>;
222 // owe the matrix, heavy object
223 matrix_type m_matrix;
224 // don't owe the vector
225 std::vector<point_ID_type> const &m_points;
226 // don't owe the space
227 std::vector<coord_type> const &m_coords;
228 // oops what was that again ?
229 mutable std::map<coord_type, std::vector<coord_type>> m_arrival_space_cash;
240 template <typename F> matrix_type make_weight_matrix(std::vector<coord_type> const &coords, F w)
241 {
242 matrix_type A(coords.size(), coords.size());
243 for (unsigned i = 0; i < A.size1(); ++i)
244 {
245 for (unsigned j = 0; j < A.size2(); ++j)
246 {
247 auto x = coords.at(i);
248 auto y = coords.at(j);
249 A(i, j) = w(x, y);
250 }
251 }
252 return quetzal::utils::divide_terms_by_row_sum(A);
253 }
254
255 public:
259 Implementation(const Implementation &) = delete;
263 template <typename F>
264 Implementation(std::vector<point_ID_type> const &points, std::vector<coord_type> const &coords, F f)
265 : m_matrix(make_matrix(coords, f)), m_points(points), m_coords(coords)
266 {
267 }
276 auto arrival_space(coord_type const &x) const
277 {
278 auto it = m_arrival_space_cash.find(x);
279 if (it != m_arrival_space_cash.end())
280 {
281 return it->second;
282 }
283 else
284 {
285 auto p = m_arrival_space_cash.emplace(x, retrieve_non_zero_arrival_space(x));
286 return p.first->second;
287 }
288 }
292 auto retrieve_non_zero_arrival_space(coord_type const &x) const
293 {
294 using it1_t = typename matrix_type::const_iterator1;
295 using it2_t = typename matrix_type::const_iterator2;
296 auto i = quetzal::utils::getIndexOfPointInVector(x, m_coords);
297 it1_t it1 = m_matrix.begin1();
298 std::advance(it1, i);
299 std::vector<coord_type> v;
300 v.reserve(m_matrix.size2()); // worst case
301 for (it2_t it2 = it1.begin(); it2 != it1.end(); it2++)
302 {
303 if (*it2 > 0.0)
304 v.push_back(m_coords.at(it2.index2()));
305 }
306 return v;
307 }
311 double migration_rate(coord_type const &x, coord_type const &y)
312 {
313 return m_matrix(quetzal::utils::getIndexOfPointInVector(x, m_coords),
315 }
316 }; // end of inner class Implementation
317
321 template <typename Space, typename Matrix> class Interface
322 {
323 private:
324 using impl_type = Implementation<Space, Matrix>;
325 using coord_type = Space;
326 using point_ID_type = quetzal::utils::PointWithId<coord_type>;
327 std::shared_ptr<impl_type> m_pimpl;
328
329 public:
334 template <typename F>
335 Interface(std::vector<point_ID_type> const &points, std::vector<coord_type> const &coords, F f)
336 : m_pimpl(std::make_shared<impl_type>(points, coords, f))
337 {
338 }
342 double operator()(coord_type const &x, coord_type const &y)
343 {
344 return m_pimpl->migration_rate(x, y);
345 }
351 auto arrival_space(coord_type const &x) const
352 {
353 return m_pimpl->arrival_space(x);
354 }
355 }; // inner class Interface
356
357 public:
359 using value_type = unsigned int;
368 template <typename T, typename F> static auto make_distance_based_dispersal(std::vector<T> const &coords, F f)
369 {
370 using coord_type = T;
371 using point_ID_type = quetzal::utils::PointWithId<coord_type>;
372 std::vector<point_ID_type> points;
373 points.reserve(coords.size());
374 const auto &ref = coords;
375 std::transform(coords.begin(), coords.end(), std::back_inserter(points),
376 [ref](coord_type const &x) { return point_ID_type(ref, x); });
377 using matrix_type = boost::numeric::ublas::symmetric_matrix<double>;
378 return Interface<coord_type, matrix_type>(points, coords, f);
379 }
380
392 template <typename T, typename F>
393 static auto make_sparse_distance_based_dispersal(std::vector<T> const &coords, F f)
394 {
395 using coord_type = T;
396 using point_ID_type = quetzal::utils::PointWithId<coord_type>;
397 std::vector<point_ID_type> points;
398 points.reserve(coords.size());
399 const auto &ref = coords;
400 std::transform(coords.begin(), coords.end(), std::back_inserter(points),
401 [ref](coord_type const &x) { return point_ID_type(ref, x); });
402 using matrix_type = boost::numeric::ublas::compressed_matrix<double>;
403 return Interface<coord_type, matrix_type>(points, coords, f);
404 }
405
423 template <typename Space, typename F1, typename F2> class neighboring_migration
424 {
425 private:
426 double m_emigrant_rate;
427 F1 m_friction;
428 F2 m_get_neighbors;
429
430 public:
431 using coord_type = Space;
432 // Constructor
433 neighboring_migration(double emigrant_rate, F1 friction, F2 get_neighbors)
434 : m_emigrant_rate(emigrant_rate), m_friction(friction), m_get_neighbors(get_neighbors)
435 {
436 }
442 std::vector<coord_type> arrival_space(coord_type const &x) const
443 {
444 auto v = m_get_neighbors(x);
445 // let's add focal deme to have all possible arrival demes
446 v.push_back(x);
447 return v;
448 }
452 auto operator()(coord_type const &from, coord_type const &to)
453 {
454 if (from == to)
455 {
456 return 1.0 - m_emigrant_rate;
457 }
458 else
459 {
460 return m_emigrant_rate * friction_weights(from, to);
461 }
462 }
463
464 private:
465 double friction_weights(coord_type const &x, coord_type const &y) const
466 {
467 // TODO does it copy the whole vector?
468 auto neighbors = m_get_neighbors(x);
469 std::vector<double> v(neighbors.size(), 0.0);
470 std::transform(neighbors.begin(), neighbors.end(), v.begin(),
471 [this](coord_type const &z) { return 1.0 / (this->m_friction(z)); });
472 double sum = std::accumulate(v.begin(), v.end(), 0.0);
473 return 1.0 / (m_friction(y) * sum);
474 }
475 }; // end neighboring_migration
491 template <typename T, typename U, typename V>
492 static neighboring_migration<T, U, V> make_neighboring_migration(T, double emigrant_rate, U friction,
493 V get_neighbors)
494 {
495 return neighboring_migration<T, U, V>(emigrant_rate, friction, get_neighbors);
496 }
497}; // end class mass_based
498} // end namespace demographic_policy
499} // end namespace demography
500} // end namespace quetzal
Policy class to specialize the quetzal::demography::History class for expansion of populations with v...
Definition demographic_policy.hpp:40
static auto make_distance_based_dispersal(std::vector< T > const &coords, F f)
Build a dispersal kernel usable with class History specialized for individual-based policy.
Definition demographic_policy.hpp:184
Dispersal kernel compatible with the mass-based policy, but that restricts migration to the vecinity ...
Definition demographic_policy.hpp:424
std::vector< coord_type > arrival_space(coord_type const &x) const
Defines arrival space (that is in this class case, vecinity plus focal deme) from coordinate x.
Definition demographic_policy.hpp:442
auto operator()(coord_type const &from, coord_type const &to)
Migration rate, operator interfaceable with History algorithm.
Definition demographic_policy.hpp:452
Policy class to specialize the quetzal::demography::History class for simulation of population with l...
Definition demographic_policy.hpp:207
static neighboring_migration< T, U, V > make_neighboring_migration(T, double emigrant_rate, U friction, V get_neighbors)
Helper function to create a dispersal kernel compatible with History specialized for the mass-based p...
Definition demographic_policy.hpp:492
static auto make_distance_based_dispersal(std::vector< T > const &coords, F f)
Build a dispersal kernel usable with class History specialized for mass-based simulation policy.
Definition demographic_policy.hpp:368
static auto make_sparse_distance_based_dispersal(std::vector< T > const &coords, F f)
Contruct a dispersal kernel compatible with the mass-based policy.
Definition demographic_policy.hpp:393
Class to associate an indexing semantics to a collection of points.
Definition PointWithId.hpp:37
size_t getIndexOfPointInVector(Point const &p, std::vector< Point > const &vect)
Find the index of a point in a vector.
Definition PointWithId.hpp:22
Simulation of coalescence-based models of molecular evolution.
Definition coalescence.hpp:21