Skip to content

Commit

Permalink
doc: direct initialization of spatial graph edges
Browse files Browse the repository at this point in the history
  • Loading branch information
Becheler committed Sep 25, 2024
1 parent a1b7e5c commit ba77933
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 11 deletions.
2 changes: 1 addition & 1 deletion .devcontainer/Dockerfile-ubuntu
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM ubuntu:23.04
FROM ubuntu:24.04

ARG DEBIAN_FRONTEND=noninteractive

Expand Down
39 changes: 33 additions & 6 deletions docs/4-tutorials.md
Original file line number Diff line number Diff line change
Expand Up @@ -1106,7 +1106,7 @@ Where:
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 <a href="nathan-et-al-2012.pdf" target="_blank"><b> Nathan et al. 2012</b></a>). 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.
### Basics
### 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,
\f$a\f$, which is consistent with a distance unit, and a shape parameter,
Expand All @@ -1126,7 +1126,7 @@ Quetzal incorporates various kernel types available in the `quetzal::demography:
---
### Using Kernels with Spatial Graphs
### 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.
Expand All @@ -1142,12 +1142,25 @@ Here we show how to simply print out the probabilities computed at each edge of
---
### Using Kernels with Spatial Graphs
### Using Kernels with Spatial Graphs Edges
In the simulation context, it can be a better alternative to compute the dispersal probabilities along the edges
of the graph once for all - especially if those are constant through time.
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.
Here we show how to modify the edge property in order to conserve the probabilities computed at each edge of the spatial graph.
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**
Expand All @@ -1156,3 +1169,17 @@ Here we show how to modify the edge property in order to conserve the probabilit
**Output**
@include{lineno} geography_dispersal_kernel_3.txt
#### 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 default 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**
@include{lineno} geography_dispersal_kernel_4.cpp
**Output**
@include{lineno} geography_dispersal_kernel_4.txt
53 changes: 53 additions & 0 deletions example/geography_dispersal_kernel_4.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#include "quetzal/quetzal.hpp"

#include <cassert>
#include <filesystem>
#include <ranges>

namespace geo = quetzal::geography;
using namespace mp_units::si::unit_symbols; // SI system: km, m, s

using kernel_type = quetzal::demography::dispersal_kernel::exponential_power<>;

struct edge_info
{
using probability_type = kernel_type::pdf_result_type;
probability_type weight;
auto compute(auto source, auto target, const auto & grid)
{
auto r = grid.to_latlon(source).great_circle_distance_to(grid.to_latlon(target));
return kernel_type::pdf( r, { .a=200.*km , .b=5.5 } );
}
edge_info(auto source, auto target, const auto & grid): weight(compute(source, target, grid)) {}
edge_info() = default;
};

int main()
{
auto file1 = std::filesystem::current_path() / "data/bio1.tif";
auto file2 = std::filesystem::current_path() / "data/bio12.tif";

// The raster have 10 bands that we will assign to 2001 ... 2010.
std::vector<int> times(10);
std::iota(times.begin(), times.end(), 2001);

// Initialize the landscape: for each var a key and a file, for all a time series.
using landscape_type = geo::landscape<>;
auto land = landscape_type::from_file({{"bio1", file1}, {"bio12", file2}}, times);

// Edges will store the result of the kernel's probability distribution function
using vertex_info = geo::no_property;

// We convert the grid to a graph, passing our assumptions
auto graph = geo::from_grid(land,
vertex_info(),
edge_info(),
geo::connect_fully(),
geo::isotropy(),
geo::mirror());

// Print the result
for (const auto& e : graph.edges() ) {
std::cout << e << " : " << graph[e].weight << '\n';
}
}
23 changes: 19 additions & 4 deletions src/include/quetzal/geography/graph/from_grid.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,22 @@ namespace quetzal::geography
/// @param directionality A policy class giving the directionality (isotropy or anisotropy)
/// @param bound A BoundPolicy
/// @return A graph with the desired level of connectivity
template <two_dimensional SpatialGrid, class VertexProperty, class EdgeProperty, class Vicinity,
directional Directionality, class Policy>
template<
two_dimensional SpatialGrid,
directional Directionality,
class VertexProperty,
class EdgeProperty,
class Vicinity,
class Policy
>
auto from_grid(SpatialGrid const &grid, VertexProperty const &v, EdgeProperty const &e, Vicinity const &vicinity,
Directionality dir, Policy const &bounding_policy)
{
using graph_type = quetzal::geography::graph<VertexProperty, EdgeProperty, typename Vicinity::connectedness, Directionality>;
namespace geo = quetzal::geography;
using connectedness = Vicinity::connectedness;
using graph_type = geo::graph<VertexProperty, EdgeProperty, connectedness, Directionality>;
using vertex_t = graph_type::vertex_descriptor;

graph_type graph( grid.width() * grid.height() + bounding_policy.num_extra_vertices() ) ;
vicinity.connect(graph, grid, bounding_policy);

Expand All @@ -42,7 +52,12 @@ auto from_grid(SpatialGrid const &grid, VertexProperty const &v, EdgeProperty co

if constexpr ( ! std::is_same_v<EdgeProperty, no_property>) {
for(auto edge : graph.edges()){
graph[edge] = e;
if constexpr (std::constructible_from<VertexProperty, vertex_t, vertex_t, SpatialGrid>){
graph[edge] = EdgeProperty(edge.source(), edge.target(), grid);
} else {
static_assert(std::is_default_constructible_v<EdgeProperty>);
graph[edge] = e;
}
}
}

Expand Down

0 comments on commit ba77933

Please sign in to comment.