Quetzal-CoaTL
The Coalescence Template Library
Loading...
Searching...
No Matches
Dispersal on a spatial graph

Distance-Based Kernels

A straightforward approach to determine the dispersal mode across a complete spatial graph involves correlating the geographic distance between two locations with the likelihood of dispersal between them. The objective of this section is to highlight this model choice by parametrizing a simple Dispersal Location Kernel (in the sense of Nathan et al. 2012). Additionally, we will calculate essential metrics, such as the distance between two coordinates, the dispersal probability between them, and the average dispersal distance anticipated under this distribution.

Model's Basics

The dispersal location kernel represents the statistical pattern of dispersal distances within a population. In this context, it essentially serves as the probability density function (pdf) that outlines the distribution of locations after dispersal in relation to the original location. The expressions have been adjusted to incorporate a scale parameter, \(a\), which is consistent with a distance unit, and a shape parameter, \(b\), that dictates the curvature of the distribution curve, emphasizing the influence of long-distance events.

For a source \((x_0,y_0)\), the dispersal location kernel denoted as \(k_L(r)\) provides the density of the probability of the dispersal end point in the 2D space. In this case, \(k_L(r)dA\) is the probability of a dispersal end point to be within a small 2D area \(dA\) around the location \((x,y)\). Since a probability is unitless and \(dA\) is an area, \(k_L(r)\) is expressed in per unit area in a 2D space.

Quetzal incorporates various kernel types available in the quetzal::demography::dispersal_kernel namespace, and streamlines compile-time dimensional analysis and units conversion using the mp-units library.

Input

1#include "quetzal/quetzal.hpp"
2#include <iostream>
3#include <mp-units/ostream.h>
4
5using namespace quetzal;
6
7int main()
8{
9 // Shorten notation
10 using namespace mp_units::si::unit_symbols; // SI system: km, m, s
13
14 // Dispersal kernels operate on distances between geographic coordinates
15 constexpr auto source = lonlat(2.25, 48.9176);
16 constexpr auto target = lonlat(2.25, 48.95);
17 constexpr auto distance = source.great_circle_distance_to(target);
18
19 // Used to parametrize the kernel
20 constexpr auto param = gaussian<>::param_type{.a = 1000. * m};
21
22 // Compute quantities
23 auto p = gaussian<>::pdf(distance, param);
24 auto d = gaussian<>::mean_dispersal_distance(param);
25
26 std::cout << "Dispersal from " << source << " to " << target << " :\n"
27 << "distance is " << distance << "\n"
28 << "probability is " << p << "\n"
29 << "mean dispersal distance is " << d << std::endl;
30}
Simulation of coalescence-based models of molecular evolution.
Definition coalescence.hpp:21
Gaussian dispersal location kernel (thin-tailed)
Definition dispersal_kernel.hpp:35
Geographic coordinates.
Definition coordinates.hpp:218

Output

1Dispersal from (Lat: 48.9176, Lon: 2.25) to (Lat: 48.95, Lon: 2.25) :
2distance is 3602.72 m
3probability is 7.34343e-13 1/m²
4mean dispersal distance is 886.227 m

Kernels from Spatial Graphs

Users can create a spatial graph from landscape data, customizing assumptions about connectivity and topology. By defining a dispersal kernel and calculating dispersal probabilities for each edge based on distance metrics, users can uncover insights into potential dispersal patterns within the spatial graph. This approach offers a powerful means for exploring and understanding spatial dynamics and connectivity, facilitating deeper analysis of ecological or geographic phenomena.

Here we show how to simply print out the probabilities computed at each edge of the spatial graph.

Input

1#include "quetzal/quetzal.hpp"
2
3#include <cassert>
4#include <filesystem>
5#include <ranges>
6
7namespace geo = quetzal::geography;
8using namespace mp_units::si::unit_symbols; // SI system: km, m, s
9
10int main()
11{
12 auto file1 = std::filesystem::current_path() / "data/bio1.tif";
13 auto file2 = std::filesystem::current_path() / "data/bio12.tif";
14
15 // The raster have 10 bands that we will assign to 2001 ... 2010.
16 std::vector<int> times(10);
17 std::iota(times.begin(), times.end(), 2001);
18
19 // Initialize the landscape: for each var a key and a file, for all a time series.
20 using landscape_type = geo::landscape<>;
21 auto land = landscape_type::from_file({{"bio1", file1}, {"bio12", file2}}, times);
22
23 // Our graph will not store any useful information
26
27 // We convert the grid to a graph, passing our assumptions
29 geo::mirror());
30
31 // Define a helper function to compute distance on Earth between two location_descriptors
32 auto sphere_distance = [&](auto point1, auto point2)
33 {
34 return land.to_latlon(point1).great_circle_distance_to(land.to_latlon(point2));
35 };
36
37 // Define the type of distance-based kernel to use
39
40 // Transform the edges of the graph to a vector of probability to disperse from source to target
41 auto probabilities = graph.edges()
42 | std::views::transform( [&](auto const& e){ return sphere_distance( graph.source( e ), graph.target( e ) );} )
43 | std::views::transform( [&](auto distance){ return kernel::pdf( distance, { .a=200.*km , .b=5.5 } ) ;} );
44
45 // Print the result
46 for (auto const& i : probabilities)
47 std::cout << i << '\n';
48}
Discrete spatio-temporal variations of a set of environmental variables.
Definition landscape.hpp:42
Individuals can not escape the landscape's borders.
Definition bound_policy.hpp:20
Geospatial data formatting and processing.
Definition geography.hpp:17
boost::no_property no_property
Represents no information carried by vertices or edges of a graph.
Definition concepts.hpp:71
auto from_grid(SpatialGrid const &grid, VertexProperty const &v, EdgeProperty const &e, Vicinity const &vicinity, Directionality dir, Policy const &bounding_policy)
Spatial graph construction method.
Definition from_grid.hpp:36
boost::undirectedS isotropy
Property of a process independent of the direction of movement.
Definition directionality.hpp:18
Definition geography_dispersal_kernel_4.cpp:13
Exponential Power dispersal location kernel ( : thin-tailed, : fat-tailed. Always thinner than powe...
Definition dispersal_kernel.hpp:164
Definition vicinity.hpp:53
Definition coalescence_binary_tree_2.cpp:5

Output

15.89588e-23 1/m²
20 1/m²
35.89588e-23 1/m²
45.58082e-132 1/m²
50 1/m²
60 1/m²
70 1/m²
85.58082e-132 1/m²
90 1/m²
101.69142e-30 1/m²
110 1/m²
120 1/m²
135.58082e-132 1/m²
140 1/m²
151.69142e-30 1/m²
160 1/m²
170 1/m²
180 1/m²
195.58082e-132 1/m²
200 1/m²
210 1/m²
220 1/m²
230 1/m²
240 1/m²
250 1/m²
265.58082e-132 1/m²
270 1/m²
281.22468e-40 1/m²
290 1/m²
300 1/m²
310 1/m²
320 1/m²
330 1/m²
345.58082e-132 1/m²
350 1/m²
361.22468e-40 1/m²

Using Kernels with Spatial Graphs Edges

In a simulation context, it can be more efficient to compute the dispersal probabilities along the graph's edges once, especially if these probabilities remain constant over time. This saves repeated computations during each simulation step.

There are two main approaches for setting edge properties:

  1. Default Initialization and Later Update: You can create the graph with default edge properties and then update those properties later by iterating over the edges. It's simple to implement, but you pay the cost of iterating twice over the edges.
  2. Direct Initialization: If you want to initialize the edge properties directly, the property itself should be a user-defined class with a proper default constructor, allowing for efficient initialization during graph creation. This approach helps optimize performance, particularly in simulations with many edges or static dispersal probabilities.

Default Initialization and Later Update

Here, we explain how to create a weighted graph with default edge properties and update those properties later by iterating over the edges.

First, when constructing the graph, each edge is initialized with a default set of properties (e.g., weights, dispersal probabilities). This allows you to quickly set up the graph structure without needing to provide specific values for every edge upfront.

Once the graph is created, you can update the edge properties by looping through each edge and assigning more accurate or context-specific values. This method is particularly useful when the final edge properties are derived from complex calculations or external data that may not be immediately available at graph creation.

By initializing the graph with default values, you maintain flexibility, allowing you to efficiently adjust edge properties later in the simulation or process without needing to recreate the entire graph structure.

Input

1#include "quetzal/quetzal.hpp"
2
3#include <cassert>
4#include <filesystem>
5#include <ranges>
6
7namespace geo = quetzal::geography;
8using namespace mp_units::si::unit_symbols; // SI system: km, m, s
9
10int main()
11{
12 auto file1 = std::filesystem::current_path() / "data/bio1.tif";
13 auto file2 = std::filesystem::current_path() / "data/bio12.tif";
14
15 // The raster have 10 bands that we will assign to 2001 ... 2010.
16 std::vector<int> times(10);
17 std::iota(times.begin(), times.end(), 2001);
18
19 // Initialize the landscape: for each var a key and a file, for all a time series.
20 using landscape_type = geo::landscape<>;
21 auto land = landscape_type::from_file({{"bio1", file1}, {"bio12", file2}}, times);
22
23 // Define the type of distance-based kernel to use
25
26 // Edges will store the result of the kernel's probability distribution function
28 using edge_info = kernel::pdf_result_type;
29
30 // We convert the grid to a graph, passing our assumptions
32 geo::mirror());
33
34 // Define a helper function to compute distance on Earth between two location_descriptors
35 auto sphere_distance = [&](auto point1, auto point2){
36 return land.to_latlon(point1).great_circle_distance_to(land.to_latlon(point2));
37 };
38
39 // Transform the edges of the graph to a vector of probability to disperse from source to target
40 for (const auto& e : graph.edges() ) {
41 auto r = sphere_distance( graph.source( e ), graph.target( e ) );
42 graph[e] = kernel::pdf( r, { .a=200.*km , .b=5.5 } );
43 }
44
45 // Print the result
46 for (const auto& e : graph.edges() ) {
47 std::cout << e << " : " << graph[e] << '\n';
48 }
49}

Output

1(1,0) : 5.89588e-23 1/m²
2(2,0) : 0 1/m²
3(2,1) : 5.89588e-23 1/m²
4(3,0) : 5.58082e-132 1/m²
5(3,1) : 0 1/m²
6(3,2) : 0 1/m²
7(4,0) : 0 1/m²
8(4,1) : 5.58082e-132 1/m²
9(4,2) : 0 1/m²
10(4,3) : 1.69142e-30 1/m²
11(5,0) : 0 1/m²
12(5,1) : 0 1/m²
13(5,2) : 5.58082e-132 1/m²
14(5,3) : 0 1/m²
15(5,4) : 1.69142e-30 1/m²
16(6,0) : 0 1/m²
17(6,1) : 0 1/m²
18(6,2) : 0 1/m²
19(6,3) : 5.58082e-132 1/m²
20(6,4) : 0 1/m²
21(6,5) : 0 1/m²
22(7,0) : 0 1/m²
23(7,1) : 0 1/m²
24(7,2) : 0 1/m²
25(7,3) : 0 1/m²
26(7,4) : 5.58082e-132 1/m²
27(7,5) : 0 1/m²
28(7,6) : 1.22468e-40 1/m²
29(8,0) : 0 1/m²
30(8,1) : 0 1/m²
31(8,2) : 0 1/m²
32(8,3) : 0 1/m²
33(8,4) : 0 1/m²
34(8,5) : 5.58082e-132 1/m²
35(8,6) : 0 1/m²
36(8,7) : 1.22468e-40 1/m²

Direct Initialization

Here, we demonstrate how to directly initialize the edge properties during graph creation. This approach is particularly useful when working with large graphs, where manually updating each edge later would be inefficient. By initializing the properties in place, you can ensure that each edge has the correct values from the start.

This method requires defining a user-specific Edge Property class, which includes a constructor to allow for proper instantiation. Doing so enables you to efficiently assign properties such as dispersal probabilities or weights directly to the graph’s edges during the initial setup, streamlining the overall process.

Input

1#include "quetzal/quetzal.hpp"
2
3#include <cassert>
4#include <filesystem>
5#include <ranges>
6
7namespace geo = quetzal::geography;
8using namespace mp_units::si::unit_symbols; // SI system: km, m, s
9
11
12struct edge_info
13{
14 using probability_type = kernel_type::pdf_result_type;
15 probability_type weight;
16 auto compute(auto source, auto target, const auto & grid)
17 {
18 auto r = grid.to_latlon(source).great_circle_distance_to(grid.to_latlon(target));
19 return kernel_type::pdf( r, { .a=200.*km , .b=5.5 } );
20 }
21 edge_info(auto source, auto target, const auto & grid): weight(compute(source, target, grid)) {}
22 edge_info() = default;
23};
24
25int main()
26{
27 auto file1 = std::filesystem::current_path() / "data/bio1.tif";
28 auto file2 = std::filesystem::current_path() / "data/bio12.tif";
29
30 // The raster have 10 bands that we will assign to 2001 ... 2010.
31 std::vector<int> times(10);
32 std::iota(times.begin(), times.end(), 2001);
33
34 // Initialize the landscape: for each var a key and a file, for all a time series.
35 using landscape_type = geo::landscape<>;
36 auto land = landscape_type::from_file({{"bio1", file1}, {"bio12", file2}}, times);
37
38 // Edges will store the result of the kernel's probability distribution function
40
41 // We convert the grid to a graph, passing our assumptions
42 auto graph = geo::from_grid(land,
43 vertex_info(),
44 edge_info(),
47 geo::mirror());
48
49 // Print the result
50 for (const auto& e : graph.edges() ) {
51 std::cout << e << " : " << graph[e].weight << '\n';
52 }
53}
quantity< inverse(pow< 2 >(Distance::reference)), typename Distance::rep > pdf_result_type
Probability density function value type.
Definition dispersal_kernel.hpp:166
static constexpr pdf_result_type pdf(Distance r, param_type const &p)
Probability density function.
Definition dispersal_kernel.hpp:182

Output

1(1,0) : 0 1/m²
2(2,0) : 0 1/m²
3(2,1) : 0 1/m²
4(3,0) : 0 1/m²
5(3,1) : 0 1/m²
6(3,2) : 0 1/m²
7(4,0) : 0 1/m²
8(4,1) : 0 1/m²
9(4,2) : 0 1/m²
10(4,3) : 0 1/m²
11(5,0) : 0 1/m²
12(5,1) : 0 1/m²
13(5,2) : 0 1/m²
14(5,3) : 0 1/m²
15(5,4) : 0 1/m²
16(6,0) : 0 1/m²
17(6,1) : 0 1/m²
18(6,2) : 0 1/m²
19(6,3) : 0 1/m²
20(6,4) : 0 1/m²
21(6,5) : 0 1/m²
22(7,0) : 0 1/m²
23(7,1) : 0 1/m²
24(7,2) : 0 1/m²
25(7,3) : 0 1/m²
26(7,4) : 0 1/m²
27(7,5) : 0 1/m²
28(7,6) : 0 1/m²
29(8,0) : 0 1/m²
30(8,1) : 0 1/m²
31(8,2) : 0 1/m²
32(8,3) : 0 1/m²
33(8,4) : 0 1/m²
34(8,5) : 0 1/m²
35(8,6) : 0 1/m²
36(8,7) : 0 1/m²