vignettes/raptr.Rmd
raptr.Rmd
This vignette illustrates the basic usage of the raptr R package.
To load the raptr R package and learn more about the package, type the following code into R.
# load raptr R package
library(raptr)
# show package overview
?raptr
The raptr R package uses a range of S4 classes to store conservation planning data, parameters, and prioritizations (Table 1).
Table 1: Main classes in the raptr R package
Class | Description |
---|---|
ManualOpts |
place-holder class for manually specified solutions |
GurobiOpts |
stores parameters for solving optimization problems using Gurobi |
RapUnreliableOpts |
stores control variables parameters for the unreliable problem formulation |
RapReliableOpts |
stores control variables for the reliable problem formulation |
DemandPoints |
stores the coordinates and weights for a given species and attribute space |
PlanningUnitPoints |
stores the coordinates and ids for planning units that can be used to preserve a given species |
AttributeSpace |
stores the coordinates for planning units and the demand points for each species |
RapData |
stores the all the planning unit, species, and attribute space data |
RapUnsolved |
stores all the data, control variables, and parameters needed to generate prioritizations |
RapResults |
stores the prioritizations and summary statistics generated after solving a problem |
RapSolved |
stores the input data and output results |
This tutorial is designed to provide users with an understanding of how to use the raptr R package to generate and compare solutions. This tutorial uses several additional packages, so first we will run the following code to load them.
# load packages for tutorial
library(parallel)
library(plyr)
library(dplyr)
library(ggplot2)
library(RandomFields)
library(rgeos)
# set seed for reproducibility
set.seed(500)
# set number of threads for computation
threads <- as.integer(max(1, detectCores() - 2))
Now we will check if the the Gurobi software suite and the gurobi R package are installed. To do this, run the following code.
is.GurobiInstalled(verbose = TRUE)
## [1] TRUE
To investigate the behavior of the problem, we will generate prioritizations for three simulated species. We will use the unreliable formulation of the problem to understand the basics, and later move onto the reliable formulation. The first species (termed ‘uniform’) will represent a hyper-generalist. This species will inhabit all areas with equal probability. The second species (termed ‘normal’) will represent a species with a single range core. The third species (termed ‘bimodal’) will represent a species with two distinct ecotypes, each with their own range core. To reduce computational time for this example, we will use a 10 \(\times\) 10 grid of square planning units.
# make planning units
sim_pus <- sim.pus(100L)
# simulate species distributions
sim_spp <- lapply(c("uniform", "normal", "bimodal"), sim.species, n = 1,
x = sim_pus, res = 1)
Let’s see what these species’ distributions look like.
# plot species
plot(stack(sim_spp),
main = c("Uniform species", "Normal species", "Bimodal species"),
addfun = function() lines(sim_pus), nc = 3)
Next, we will generate a set of demand points. To understand the effects of probabilities and weights on the demand points, we will generate the demand points in geographic space. These demand points will be the centroids of the planning units. Additionally, we will use the same set of demand points for each species and only vary the weights of the demand points between species. Note that we are only using the same distribution of demand points for different species for teaching purposes. It is strongly recommended to use different demand points for different species in real-world conservation planning exercises. See the case-study section of this tutorial for examples on how to generate suitable demand points.
# generate coordinates for pus/demand points
pu_coords <- gCentroid(sim_pus, byid = TRUE)
# calculate weights
sim_dps <- lapply(sim_spp, function(x) extract(x, pu_coords))
# create demand point objects
sim_dps <- lapply(sim_dps, function(x) DemandPoints(pu_coords@coords,
weights = c(x)))
Now, we will construct a RapUnsolved
object to store our input data and parameters. This contains all the information to generate prioritizations.
## create RapUnreliableOpts object
# this stores parameters for the unreliable formulation problem (ie. BLM)
sim_ro <- RapUnreliableOpts()
## create RapData object
# create data.frame with species info
species <- data.frame(name = c("uniform", "normal", "bimodal"))
## create data.frame with species and space targets
# amount targets at 20% (denoted with target=0)
# space targets at 20% (denoted with target=1)
targets <- expand.grid(species = 1:3, target = 0:1, proportion = 0.2)
# calculate probability of each species in each pu
pu_probabilities <- calcSpeciesAverageInPus(sim_pus, stack(sim_spp))
## create AttributeSpace object
# this stores the coordinates of the planning units in an attribute space
# and the coordinates and weights of demand points in the space
pu_points <- PlanningUnitPoints(coords = pu_coords@coords,
ids = seq_len(nrow(sim_pus@data)))
attr_spaces <- AttributeSpaces(
list(AttributeSpace(planning.unit.points = pu_points,
demand.points = sim_dps[[1]],
species = 1L),
AttributeSpace(planning.unit.points = pu_points,
demand.points = sim_dps[[2]],
species = 2L),
AttributeSpace(planning.unit.points = pu_points,
demand.points = sim_dps[[3]],
species = 3L)),
name = "geographic")
# generate boundary data information
boundary <- calcBoundaryData(sim_pus)
## create RapData object
# this stores all the input data for the prioritization
sim_rd <- RapData(sim_pus@data, species, targets, pu_probabilities,
list(attr_spaces), boundary, SpatialPolygons2PolySet(sim_pus))
## create RapUnsolved object
# this stores all the input data and parameters needed to generate
# prioritizations
sim_ru <- RapUnsolved(sim_ro, sim_rd)
To investigate the effects of space-based targets, we will generate a prioritization for each species using only amount-based targets and compare them to prioritizations generated using amount- and space-based targets. To start off, we will generate a prioritization for the uniform species using amount-based targets. To do this, we will generate a new sim_ru
object by extracting out the data for the uniform species from the sim_ru
object. Then, we will update the targets in the new object. Finally, we will solve the object to generate a prioritization that fulfills the targets for minimal cost.
# create new object with just the uniform species
sim_ru_s1 <- spp.subset(sim_ru, "uniform")
# update amount targets to 20% and space targets to 0%
sim_ru_s1 <- update(sim_ru_s1, amount.target = 0.2, space.target = NA,
solve = FALSE)
options(error = function() {
traceback()
q("no", 1, FALSE)
})
# solve problem to identify prioritization
sim_rs_s1_amount <- solve(sim_ru_s1, Threads = threads)
## Gurobi Optimizer version 9.0.1 build v9.0.1rc0 (linux64)
## Optimize a model with 1 rows, 100 columns and 100 nonzeros
## Model fingerprint: 0xe910d3ec
## Variable types: 0 continuous, 100 integer (100 binary)
## Coefficient statistics:
## Matrix range [5e-01, 5e-01]
## Objective range [1e+00, 1e+00]
## Bounds range [1e+00, 1e+00]
## RHS range [1e+01, 1e+01]
## Found heuristic solution: objective 20.0000000
## Presolve removed 1 rows and 100 columns
## Presolve time: 0.00s
## Presolve: All rows and columns removed
##
## Explored 0 nodes (0 simplex iterations) in 0.00 seconds
## Thread count was 1 (of 4 available processors)
##
## Solution count 1: 20
##
## Optimal solution found (tolerance 1.00e-01)
## Best objective 2.000000000000e+01, best bound 2.000000000000e+01, gap 0.0000%
## Warning in validityMethod(object): some species have space.held values less
## than 0, and thus are poorly represented
## show summary
# note the format for this is similar to that used by Marxan
# see ?raptr::summary for details on this table
summary(sim_rs_s1_amount)
## Run_Number Status Score Cost Planning_Units Connectivity_Total
## 1 1 OPTIMAL 20 20 20 220
## Connectivity_In Connectivity_Edge Connectivity_Out
## 1 42 168 10
## Connectivity_In_Fraction
## 1 0.1909091
# show amount held
amount.held(sim_rs_s1_amount)
## uniform
## 1 0.2
# show space held
space.held(sim_rs_s1_amount)
## uniform (Space 1)
## 1 -0.2363636
Now that we have generated a prioritization, let’s see what it looks like. We can use the spp.plot
method to see how the prioritization overlaps with the uniform species’ distribution. Note that since all planning units have equal probabilities for this species, all planning units have the same fill color.
# plot the prioritization and the uniform species' distribution
spp.plot(sim_rs_s1_amount, 1, main = "Uniform species")
The prioritization for the uniform species appears to be just a random selection of planning units. This behavior is due to the fact that any prioritization with 20 planning units is optimal. By relying on just amount targets, this solution may preserve a section of the species’ range core, or just focus on the range margin, or some random part of its range–no emphasis is directed towards preserving different parts of the species’ range. This behavior highlights a fundamental limitation of just using amount-based targets. In the absence of additional criteria, conventional reserve selection problems do not contain any additional information to identify the most effective prioritization.
Now, we will generate a prioritization for the normally distributed species using amount-based targets. We will use a similar process to what we used for the uniformly distributed species, but for brevity, we will use code to generate solutions immediately after updating the object.
# create new object with just the normal species
sim_ru_s2 <- spp.subset(sim_ru, "normal")
# update amount targets to 20% and space targets to 0% and solve it
sim_rs_s2_amount <- update(sim_ru_s2, amount.target = 0.2, space.target = NA,
solve = TRUE, Threads = threads)
## Gurobi Optimizer version 9.0.1 build v9.0.1rc0 (linux64)
## Optimize a model with 1 rows, 100 columns and 100 nonzeros
## Model fingerprint: 0x3d4ec257
## Variable types: 0 continuous, 100 integer (100 binary)
## Coefficient statistics:
## Matrix range [7e-02, 7e-01]
## Objective range [1e+00, 1e+00]
## Bounds range [1e+00, 1e+00]
## RHS range [7e+00, 7e+00]
## Found heuristic solution: objective 27.0000000
## Presolve removed 0 rows and 86 columns
## Presolve time: 0.00s
## Presolved: 1 rows, 14 columns, 14 nonzeros
## Variable types: 0 continuous, 14 integer (0 binary)
## Presolved: 1 rows, 14 columns, 14 nonzeros
##
##
## Root relaxation: objective 9.864476e+00, 6 iterations, 0.00 seconds
##
## Nodes | Current Node | Objective Bounds | Work
## Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time
##
## 0 0 9.86448 0 1 27.00000 9.86448 63.5% - 0s
## H 0 0 14.0000000 9.86448 29.5% - 0s
## H 0 0 10.0000000 9.86448 1.36% - 0s
## 0 0 9.86448 0 1 10.00000 9.86448 1.36% - 0s
##
## Explored 1 nodes (6 simplex iterations) in 0.00 seconds
## Thread count was 2 (of 4 available processors)
##
## Solution count 3: 10 14 27
##
## Optimal solution found (tolerance 1.00e-01)
## Best objective 1.000000000000e+01, best bound 1.000000000000e+01, gap 0.0000%
# show summary
summary(sim_rs_s2_amount)
## Run_Number Status Score Cost Planning_Units Connectivity_Total
## 1 1 OPTIMAL 10 10 10 220
## Connectivity_In Connectivity_Edge Connectivity_Out
## 1 12 192 16
## Connectivity_In_Fraction
## 1 0.05454545
# show amount held
amount.held(sim_rs_s2_amount)
## normal
## 1 0.2026153
# show space held
space.held(sim_rs_s2_amount)
## normal (Space 1)
## 1 0.5909494
Now let’s visualize the prioritization we made for the normal species.
# plot the prioritization and the normal species' distribution
spp.plot(sim_rs_s2_amount, 1, main = "Normal species")
The amount-based prioritization for the normal species focuses only on the species’ range core. This prioritization fails to secure any peripheral parts of the species’ distribution. As a consequence, it may miss out on populations with novel adaptations to environmental conditions along the species’ range margin.
Now, let’s generate an amount-based target for the bimodally distributed species view it.
# create new object with just the bimodal species
sim_ru_s3 <- spp.subset(sim_ru, "bimodal")
# update amount targets to 20% and space targets to 0% and solve it
sim_rs_s3_amount <- update(sim_ru_s3, amount.target = 0.2,
space.target = NA, Threads = threads)
## Gurobi Optimizer version 9.0.1 build v9.0.1rc0 (linux64)
## Optimize a model with 1 rows, 100 columns and 100 nonzeros
## Model fingerprint: 0x1ce55139
## Variable types: 0 continuous, 100 integer (100 binary)
## Coefficient statistics:
## Matrix range [7e-03, 9e-01]
## Objective range [1e+00, 1e+00]
## Bounds range [1e+00, 1e+00]
## RHS range [7e+00, 7e+00]
## Found heuristic solution: objective 21.0000000
## Presolve removed 0 rows and 75 columns
## Presolve time: 0.00s
## Presolved: 1 rows, 25 columns, 25 nonzeros
## Variable types: 0 continuous, 25 integer (0 binary)
## Presolved: 1 rows, 25 columns, 25 nonzeros
##
##
## Root relaxation: objective 7.919039e+00, 17 iterations, 0.00 seconds
##
## Nodes | Current Node | Objective Bounds | Work
## Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time
##
## 0 0 7.91904 0 1 21.00000 7.91904 62.3% - 0s
## H 0 0 16.0000000 7.91904 50.5% - 0s
## H 0 0 8.0000000 7.91904 1.01% - 0s
## 0 0 7.91904 0 1 8.00000 7.91904 1.01% - 0s
##
## Explored 1 nodes (17 simplex iterations) in 0.00 seconds
## Thread count was 2 (of 4 available processors)
##
## Solution count 3: 8 16 21
##
## Optimal solution found (tolerance 1.00e-01)
## Best objective 8.000000000000e+00, best bound 8.000000000000e+00, gap 0.0000%
# plot the prioritization and the bimodal species' distribution
spp.plot(sim_rs_s3_amount, 1, main = "Bimodal species")
# show summary
summary(sim_rs_s3_amount)
## Run_Number Status Score Cost Planning_Units Connectivity_Total
## 1 1 OPTIMAL 8 8 8 220
## Connectivity_In Connectivity_Edge Connectivity_Out
## 1 9 197 14
## Connectivity_In_Fraction
## 1 0.04090909
# show amount held
amount.held(sim_rs_s3_amount)
## bimodal
## 1 0.2018391
# show space held
space.held(sim_rs_s3_amount)
## bimodal (Space 1)
## 1 0.1200278
The amount-based prioritization for the bimodally distributed species only selects planning units in the bottom left corner of the study area. This prioritization only preserves individuals belonging to one of the two ecotypes. As a consequence, this prioritization may fail to preserve a representative sample of the genetic variation found inside this species.
Now that we have generated a prioritization for each species using only amount-based targets, we will generate a prioritizations using both amount-based and space-targets. To do this we will update the space targets in our amount-based prioritizations to 85%, and store the new prioritizations in new objects.
First, let’s do this for the uniform species.
# make new prioritization
sim_rs_s1_space <- update(sim_rs_s1_amount, amount.target = 0.2,
space.target = 0.85, Threads = threads)
## Gurobi Optimizer version 9.0.1 build v9.0.1rc0 (linux64)
## Optimize a model with 10102 rows, 10100 columns and 40000 nonzeros
## Model fingerprint: 0x3f02f09a
## Variable types: 0 continuous, 10100 integer (10100 binary)
## Coefficient statistics:
## Matrix range [5e-01, 8e+01]
## Objective range [1e+00, 1e+00]
## Bounds range [1e+00, 1e+00]
## RHS range [1e+00, 1e+02]
## Found heuristic solution: objective 95.0000000
## Presolve removed 24 rows and 0 columns (presolve time = 5s) ...
## Presolve removed 30 rows and 0 columns (presolve time = 10s) ...
## Presolve removed 36 rows and 0 columns
## Presolve time: 12.96s
## Presolved: 10066 rows, 10100 columns, 40104 nonzeros
## Variable types: 0 continuous, 10100 integer (10100 binary)
## Presolved: 10066 rows, 10100 columns, 40104 nonzeros
##
##
## Root simplex log...
##
## Iteration Objective Primal Inf. Dual Inf. Time
## 0 1.0000000e+02 0.000000e+00 1.000000e+02 13s
## 621 2.0000000e+01 0.000000e+00 0.000000e+00 13s
## 621 2.0000000e+01 0.000000e+00 0.000000e+00 13s
##
## Root relaxation: objective 2.000000e+01, 621 iterations, 0.25 seconds
##
## Nodes | Current Node | Objective Bounds | Work
## Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time
##
## * 0 0 0 20.0000000 20.00000 0.00% - 13s
##
## Explored 0 nodes (1158 simplex iterations) in 13.58 seconds
## Thread count was 2 (of 4 available processors)
##
## Solution count 2: 20 95
##
## Optimal solution found (tolerance 1.00e-01)
## Best objective 2.000000000000e+01, best bound 2.000000000000e+01, gap 0.0000%
# show summary
summary(sim_rs_s1_space)
## Run_Number Status Score Cost Planning_Units Connectivity_Total
## 1 1 OPTIMAL 20 20 20 220
## Connectivity_In Connectivity_Edge Connectivity_Out
## 1 13 147 60
## Connectivity_In_Fraction
## 1 0.05909091
# show amount held
amount.held(sim_rs_s1_space)
## uniform
## 1 0.2
# show space held
space.held(sim_rs_s1_space)
## uniform (Space 1)
## 1 0.909697
Let’s take a look at the prioritization for the uniform species with amount-based and space-based targets. Then, let’s compare the solutions for the amount-based prioritization with the new prioritization using both amount and space targets.
# plot the prioritization and the uniform species' distribution
spp.plot(sim_rs_s1_space, "uniform", main = "Uniform species")
# plot the difference between old and new prioritizations
plot(sim_rs_s1_amount, sim_rs_s1_space, 1, 1,
main = "Difference between solutions")
Here we can see that by including a space-target, the prioritization is spread out evenly across the species’ distribution. Unlike the amount-based prioritization, this prioritization samples all the different parts of the species’ distribution.
Now, let’s generate a prioritization for the normally distributed species that considers amount-based and space-based targets. Then, let’s visualize the new prioritization and compare it to the old amount-based prioritization.
# make new prioritization
sim_rs_s2_space <- update(sim_rs_s2_amount, amount.target = 0.2,
space.target = 0.85, Threads = threads)
## Gurobi Optimizer version 9.0.1 build v9.0.1rc0 (linux64)
## Optimize a model with 10102 rows, 10100 columns and 40000 nonzeros
## Model fingerprint: 0xa48af59a
## Variable types: 0 continuous, 10100 integer (10100 binary)
## Coefficient statistics:
## Matrix range [7e-02, 4e+01]
## Objective range [1e+00, 1e+00]
## Bounds range [1e+00, 1e+00]
## RHS range [1e+00, 6e+01]
## Found heuristic solution: objective 91.0000000
## Presolve removed 211 rows and 0 columns (presolve time = 5s) ...
## Presolve removed 220 rows and 0 columns
## Presolve time: 9.89s
## Presolved: 9882 rows, 10100 columns, 41664 nonzeros
## Variable types: 0 continuous, 10100 integer (10100 binary)
## Presolved: 9882 rows, 10100 columns, 41664 nonzeros
##
##
## Root simplex log...
##
## Iteration Objective Primal Inf. Dual Inf. Time
## 0 1.0000000e+02 0.000000e+00 1.000000e+02 10s
## 4250 1.2325699e+01 0.000000e+00 0.000000e+00 11s
## 4250 1.2325699e+01 0.000000e+00 0.000000e+00 11s
##
## Root relaxation: objective 1.232570e+01, 4250 iterations, 1.22 seconds
##
## Nodes | Current Node | Objective Bounds | Work
## Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time
##
## 0 0 12.32570 0 293 91.00000 12.32570 86.5% - 11s
## H 0 0 13.0000000 12.32570 5.19% - 11s
## 0 0 12.32570 0 293 13.00000 12.32570 5.19% - 11s
##
## Explored 1 nodes (4527 simplex iterations) in 11.69 seconds
## Thread count was 2 (of 4 available processors)
##
## Solution count 2: 13 91
##
## Optimal solution found (tolerance 1.00e-01)
## Best objective 1.300000000000e+01, best bound 1.300000000000e+01, gap 0.0000%
# show summary
summary(sim_rs_s2_space)
## Run_Number Status Score Cost Planning_Units Connectivity_Total
## 1 1 OPTIMAL 13 13 13 220
## Connectivity_In Connectivity_Edge Connectivity_Out
## 1 5 173 42
## Connectivity_In_Fraction
## 1 0.02272727
# show amount held
amount.held(sim_rs_s2_space)
## normal
## 1 0.2099983
# show space held
space.held(sim_rs_s2_space)
## normal (Space 1)
## 1 0.8533832
# plot the prioritization and the normal species' distribution
spp.plot(sim_rs_s2_space, "normal", main = "Normal species")
# plot the difference between old and new prioritizations
plot(sim_rs_s2_amount, sim_rs_s2_space, 1, 1,
main = "Difference between solutions")
We can see by using both amount-based and space-based targets we can obtain a prioritization that secures both the species’ range core and parts of its range margin. As a consequence, it may capture any novel adaptations found along the species’ range margin–unlike the amount-based prioritization.
Finally, let’s generate a prioritization for the bimodal species using amount-based and space-based targets.
# make new prioritization
sim_rs_s3_space <- update(sim_rs_s3_amount, amount.target = 0.2,
space.target = 0.85, Threads = threads)
## Gurobi Optimizer version 9.0.1 build v9.0.1rc0 (linux64)
## Optimize a model with 10102 rows, 10100 columns and 40000 nonzeros
## Model fingerprint: 0xd59e1e6d
## Variable types: 0 continuous, 10100 integer (10100 binary)
## Coefficient statistics:
## Matrix range [7e-03, 9e+01]
## Objective range [1e+00, 1e+00]
## Bounds range [1e+00, 1e+00]
## RHS range [1e+00, 7e+01]
## Found heuristic solution: objective 87.0000000
## Presolve removed 515 rows and 15 columns (presolve time = 5s) ...
## Presolve removed 546 rows and 15 columns (presolve time = 10s) ...
## Presolve removed 589 rows and 15 columns (presolve time = 16s) ...
## Presolve removed 589 rows and 15 columns
## Presolve time: 18.59s
## Presolved: 9513 rows, 10085 columns, 51461 nonzeros
## Variable types: 0 continuous, 10085 integer (10085 binary)
## Presolved: 9513 rows, 10085 columns, 51461 nonzeros
##
##
## Root simplex log...
##
## Iteration Objective Primal Inf. Dual Inf. Time
## 0 1.0000000e+02 0.000000e+00 1.000000e+02 19s
## 4537 9.7543105e+00 0.000000e+00 2.828024e+02 20s
## 6104 8.9807917e+00 0.000000e+00 0.000000e+00 21s
## 6104 8.9807917e+00 0.000000e+00 0.000000e+00 21s
##
## Root relaxation: objective 8.980792e+00, 6104 iterations, 1.94 seconds
##
## Nodes | Current Node | Objective Bounds | Work
## Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time
##
## 0 0 8.98079 0 75 87.00000 8.98079 89.7% - 21s
## H 0 0 10.0000000 8.98079 10.2% - 21s
## 0 0 cutoff 0 10.00000 10.00000 0.00% - 21s
##
## Cutting planes:
## Gomory: 2
## MIR: 1
## Zero half: 2
##
## Explored 1 nodes (6445 simplex iterations) in 21.78 seconds
## Thread count was 2 (of 4 available processors)
##
## Solution count 2: 10 87
##
## Optimal solution found (tolerance 1.00e-01)
## Best objective 1.000000000000e+01, best bound 1.000000000000e+01, gap 0.0000%
# show summary
summary(sim_rs_s3_space)
## Run_Number Status Score Cost Planning_Units Connectivity_Total
## 1 1 OPTIMAL 10 10 10 220
## Connectivity_In Connectivity_Edge Connectivity_Out
## 1 7 187 26
## Connectivity_In_Fraction
## 1 0.03181818
# show amount held
amount.held(sim_rs_s3_space)
## bimodal
## 1 0.2226414
# show space held
space.held(sim_rs_s3_space)
## bimodal (Space 1)
## 1 0.8562891
# plot the prioritization and the bimodal species' distribution
spp.plot(sim_rs_s3_space, 'bimodal', main='Bimodal species')
# plot the difference between old and new prioritizations
plot(sim_rs_s3_amount, sim_rs_s3_space, 1, 1,
main = "Difference between solutions")
Earlier we found that the amount-based prioritization only preserved individuals from a single ecotype, and would have failed to adequately preserve the intra-specific variation for this species. However, here we can see that by including space-based targets, we can develop prioritizations that secure individuals belonging to both ecotypes. This new prioritization is much more effective at sampling the intra-specific variation for this species.
Overall, these results demonstrate that under the simplest of conditions, the use of space-based targets can improve prioritizations. However, these prioritizations were each generated for a single species. Prioritizations generated using multiple species may do a better job at preserving the intra-specific variation for individuals species by preserving them in different parts of their range. We will investigate this in the next section.
So far we have generated prioritizations using only a single species at a time. However, real world prioritizations are often generated using multiple species to ensure that they preserve a comprehensive set of biodiversity. Here, we will generate multi-species prioritizations that preserve all three of the simulated species. First, we will generate a prioritization using amount-based targets that only aims to preserve 20% of the area they occupy. Then, we will generate a prioritization that also incorporate space-based targets to also preserve 85% of their geographic distribution. We will then compare the two prioritizations.
# make prioritizations
sim_mrs_amount <- update(sim_ru, amount.target = c(0.2, 0.2, 0.2),
space.target = c(0, 0, 0), Threads = threads)
## Gurobi Optimizer version 9.0.1 build v9.0.1rc0 (linux64)
## Optimize a model with 30306 rows, 30100 columns and 120000 nonzeros
## Model fingerprint: 0x5ebf0f26
## Variable types: 0 continuous, 30100 integer (30100 binary)
## Coefficient statistics:
## Matrix range [7e-03, 9e+01]
## Objective range [1e+00, 1e+00]
## Bounds range [1e+00, 1e+00]
## RHS range [1e+00, 8e+02]
## Found heuristic solution: objective 98.0000000
## Presolve removed 0 rows and 0 columns (presolve time = 6s) ...
## Presolve time: 7.06s
## Presolved: 30306 rows, 30100 columns, 120000 nonzeros
## Variable types: 0 continuous, 30100 integer (30100 binary)
## Presolved: 30306 rows, 30100 columns, 120000 nonzeros
##
##
## Root simplex log...
##
## Iteration Objective Primal Inf. Dual Inf. Time
## 0 1.0000000e+02 0.000000e+00 1.000000e+02 8s
## 1890 2.3495402e+01 0.000000e+00 1.455780e+04 10s
## 2299 2.0000000e+01 0.000000e+00 0.000000e+00 11s
## 2299 2.0000000e+01 0.000000e+00 0.000000e+00 11s
##
## Root relaxation: objective 2.000000e+01, 2299 iterations, 3.91 seconds
## Total elapsed time = 15.26s
##
## Nodes | Current Node | Objective Bounds | Work
## Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time
##
## H 0 0 20.0000000 0.00000 100% - 15s
## 0 0 - 0 20.00000 20.00000 0.00% - 15s
##
## Explored 0 nodes (5871 simplex iterations) in 15.41 seconds
## Thread count was 2 (of 4 available processors)
##
## Solution count 2: 20 98
##
## Optimal solution found (tolerance 1.00e-01)
## Best objective 2.000000000000e+01, best bound 2.000000000000e+01, gap 0.0000%
sim_mrs_space <- update(sim_ru, amount.target = c(0.2, 0.2, 0.2),
space.target = c(0.85, 0.85, 0.85),
Threads = threads)
## Gurobi Optimizer version 9.0.1 build v9.0.1rc0 (linux64)
## Optimize a model with 30306 rows, 30100 columns and 120000 nonzeros
## Model fingerprint: 0xba2fdd45
## Variable types: 0 continuous, 30100 integer (30100 binary)
## Coefficient statistics:
## Matrix range [7e-03, 9e+01]
## Objective range [1e+00, 1e+00]
## Bounds range [1e+00, 1e+00]
## RHS range [1e+00, 1e+02]
## Found heuristic solution: objective 99.0000000
## Presolve removed 37 rows and 15 columns (presolve time = 5s) ...
## Presolve removed 193 rows and 15 columns (presolve time = 10s) ...
## Presolve removed 265 rows and 15 columns (presolve time = 15s) ...
## Presolve removed 267 rows and 15 columns (presolve time = 20s) ...
## Presolve removed 269 rows and 15 columns (presolve time = 25s) ...
## Presolve removed 330 rows and 15 columns (presolve time = 30s) ...
## Presolve removed 360 rows and 15 columns (presolve time = 35s) ...
## Presolve removed 360 rows and 15 columns
## Presolve time: 37.29s
## Presolved: 29946 rows, 30085 columns, 131504 nonzeros
## Variable types: 0 continuous, 30085 integer (30085 binary)
## Presolve removed 67 rows and 1 columns
## Presolved: 29879 rows, 30084 columns, 127194 nonzeros
##
##
## Root simplex log...
##
## Iteration Objective Primal Inf. Dual Inf. Time
## 0 1.0000000e+02 0.000000e+00 1.000000e+02 40s
## 5285 2.0085632e+01 0.000000e+00 6.428538e+03 45s
## 5346 2.0000000e+01 0.000000e+00 0.000000e+00 45s
## 5346 2.0000000e+01 0.000000e+00 0.000000e+00 45s
##
## Root relaxation: objective 2.000000e+01, 5346 iterations, 7.92 seconds
##
## Nodes | Current Node | Objective Bounds | Work
## Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time
##
## 0 0 20.00000 0 21 99.00000 20.00000 79.8% - 47s
## H 0 0 21.0000000 20.00000 4.76% - 47s
##
## Explored 1 nodes (10151 simplex iterations) in 47.52 seconds
## Thread count was 2 (of 4 available processors)
##
## Solution count 2: 21 99
##
## Optimal solution found (tolerance 1.00e-01)
## Best objective 2.100000000000e+01, best bound 2.000000000000e+01, gap 4.7619%
# show summaries
summary(sim_mrs_amount)
## Run_Number Status Score Cost Planning_Units Connectivity_Total
## 1 1 OPTIMAL 20 20 20 220
## Connectivity_In Connectivity_Edge Connectivity_Out
## 1 14 148 58
## Connectivity_In_Fraction
## 1 0.06363636
summary(sim_mrs_space)
## Run_Number Status Score Cost Planning_Units Connectivity_Total
## 1 1 OPTIMAL 21 21 21 220
## Connectivity_In Connectivity_Edge Connectivity_Out
## 1 6 137 77
## Connectivity_In_Fraction
## 1 0.02727273
# show amount held for each prioritization
amount.held(sim_mrs_amount)
## uniform normal bimodal
## 1 0.2 0.2472436 0.2953565
amount.held(sim_mrs_space)
## uniform normal bimodal
## 1 0.21 0.2393104 0.2269545
# show space held for each prioritization
space.held(sim_mrs_amount)
## uniform (Space 1) normal (Space 1) bimodal (Space 1)
## 1 0.8848485 0.8631116 0.9096401
space.held(sim_mrs_space)
## uniform (Space 1) normal (Space 1) bimodal (Space 1)
## 1 0.929697 0.9170622 0.9278573
# plot multi-species prioritization with amount-based targets
plot(sim_mrs_amount, 1, main = "Amount-based targets")
# plot multi-species prioritization with amount- and space-based targets
plot(sim_mrs_space, 1, main = "Amount and space-based targets")
# difference between the two prioritizations
plot(sim_mrs_amount, sim_mrs_space, 1, 1, main = "Difference between solutions")
Here we can see that the inclusion of space-based targets changes which planning units are selected for prioritization, but also the number of planning units that are selected. The amount-based prioritization is comprised of 20 units, and the space-based prioritization is comprised of 21 units. This result suggests that an adequate and representative prioritization can be achieved for only a minor increase in cost.
The unreliable formulation does not consider the probability that the planning units are occupied by features when calculating how well a given solution secures a representative sample of an attribute space. Thus solutions identified using the unreliable formulation may select regions of an attribute space for a species using planning units that only have a small chance of being inhabited. As a consequence, if the prioritization is implemented, it may fail to secure regions of an attribute space if individuals do not inhabit these planning units, and ultimately fail to fulfill the space-based targets.
A simple solution to this issue would be to ensure that planning units cannot be assigned to demand points if they have a low probability of occupancy. This can be achieved by setting a probability threshold for planning units, such that planning units with a probability of occupancy below the threshold are effectively set to zero.
# make new prioritization with probability threshold of 0.1 for each species
sim_mrs_space2 <- solve(prob.subset(sim_mrs_space, species = 1:3,
threshold = c(0.1, 0.1, 0.1)),
Threads = threads)
## Gurobi Optimizer version 9.0.1 build v9.0.1rc0 (linux64)
## Optimize a model with 30306 rows, 30100 columns and 119974 nonzeros
## Model fingerprint: 0xb1d3b678
## Variable types: 0 continuous, 30100 integer (30100 binary)
## Coefficient statistics:
## Matrix range [7e-03, 9e+01]
## Objective range [1e+00, 1e+00]
## Bounds range [1e+00, 1e+00]
## RHS range [1e+00, 1e+02]
## Found heuristic solution: objective 99.0000000
## Presolve removed 37 rows and 15 columns (presolve time = 5s) ...
## Presolve removed 160 rows and 15 columns (presolve time = 10s) ...
## Presolve removed 265 rows and 15 columns (presolve time = 15s) ...
## Presolve removed 267 rows and 15 columns (presolve time = 20s) ...
## Presolve removed 269 rows and 15 columns (presolve time = 25s) ...
## Presolve removed 329 rows and 15 columns (presolve time = 30s) ...
## Presolve removed 360 rows and 15 columns (presolve time = 36s) ...
## Presolve removed 360 rows and 15 columns
## Presolve time: 38.24s
## Presolved: 29946 rows, 30085 columns, 131478 nonzeros
## Variable types: 0 continuous, 30085 integer (30085 binary)
## Presolve removed 67 rows and 1 columns
## Presolved: 29879 rows, 30084 columns, 127168 nonzeros
##
##
## Root simplex log...
##
## Iteration Objective Primal Inf. Dual Inf. Time
## 0 1.0000000e+02 0.000000e+00 1.000000e+02 41s
## 4281 2.2280680e+01 0.000000e+00 6.683071e+03 45s
## 5257 2.0000000e+01 0.000000e+00 0.000000e+00 46s
## 5257 2.0000000e+01 0.000000e+00 0.000000e+00 46s
##
## Root relaxation: objective 2.000000e+01, 5257 iterations, 7.68 seconds
## Total elapsed time = 51.43s
##
## Nodes | Current Node | Objective Bounds | Work
## Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time
##
## 0 0 20.00000 0 22 99.00000 20.00000 79.8% - 52s
## H 0 0 21.0000000 20.00000 4.76% - 52s
##
## Explored 1 nodes (12431 simplex iterations) in 52.39 seconds
## Thread count was 2 (of 4 available processors)
##
## Solution count 2: 21 99
##
## Optimal solution found (tolerance 1.00e-01)
## Best objective 2.100000000000e+01, best bound 2.000000000000e+01, gap 4.7619%
# show summary
summary(sim_mrs_space2)
## Run_Number Status Score Cost Planning_Units Connectivity_Total
## 1 1 OPTIMAL 21 21 21 220
## Connectivity_In Connectivity_Edge Connectivity_Out
## 1 9 139 72
## Connectivity_In_Fraction
## 1 0.04090909
# plot prioritization
plot(sim_mrs_space2, 1)
# difference between prioritizations that use and do not use thresholds
plot(sim_mrs_space2, sim_mrs_space, 1, 1, main = "Difference between solutions")
But this method requires setting somewhat arbitrary thresholds. A more robust solution to this issue is to actually use the probability that species occupy planning units to generate the prioritizations. This is what the reliable formulation does. First we will try and generate a solution using existing targets and the reliable formulation. To reduce computational time, we will set the maximum backup \(R\)-level to 1.
# make new prioritization using reliable formulation
sim_mrs_space3 <- try(update(sim_mrs_space, formulation = "reliable",
max.r.level = 1L, Threads = threads))
## Gurobi Optimizer version 9.0.1 build v9.0.1rc0 (linux64)
## Optimize a model with 364206 rows, 181900 columns and 3847200 nonzeros
## Model fingerprint: 0xf44c8001
## Variable types: 0 continuous, 60700 integer (60700 binary)
## Semi-Variable types: 121200 continuous, 0 integer
## Coefficient statistics:
## Matrix range [8e-04, 1e+02]
## Objective range [1e+00, 1e+00]
## Bounds range [1e+00, 1e+00]
## RHS range [7e-03, 1e+02]
## Presolve removed 313400 rows and 131600 columns (presolve time = 6s) ...
## Presolve removed 338500 rows and 156700 columns (presolve time = 10s) ...
## Presolve removed 338506 rows and 156703 columns
## Presolve time: 13.30s
##
## Explored 0 nodes (0 simplex iterations) in 14.58 seconds
## Thread count was 1 (of 4 available processors)
##
## Solution count 0
##
## Model is infeasible
## Best objective -, best bound -, gap -
## Error in .local(a, b, ...) :
## No solution found because the problem cannot be solved because space-based targets are too high. Try setting lower space-based targets. See ?maximum.targets
However, this fails. The reason why we cannot generate a prioritization that fulfills these targets is because even the solution that contains all the planning units is still insufficient when we consider probabilities. The negative maximum targets printed in the error message indicate that planning units have low probabilities of occupancy. To fulfill the targets, we must obtain more planning units with higher probabilities of occupancy. We also could attempt resolving the problem using a higher \(R\)-level. Instead, we will set lower targets and generate solution.
# make new prioritization using reliable formulation and reduced targets
sim_mrs_space3 <- update(sim_mrs_space, formulation = "reliable",
max.r.level = 1L, space.target = -1000,
Threads = threads)
## Gurobi Optimizer version 9.0.1 build v9.0.1rc0 (linux64)
## Optimize a model with 364206 rows, 181900 columns and 3847200 nonzeros
## Model fingerprint: 0x197cd333
## Variable types: 0 continuous, 60700 integer (60700 binary)
## Semi-Variable types: 121200 continuous, 0 integer
## Coefficient statistics:
## Matrix range [8e-04, 1e+02]
## Objective range [1e+00, 1e+00]
## Bounds range [1e+00, 1e+00]
## RHS range [7e-03, 8e+05]
## Presolve removed 303003 rows and 121200 columns (presolve time = 7s) ...
## Presolve removed 333303 rows and 151500 columns (presolve time = 10s) ...
## Presolve removed 333703 rows and 151600 columns (presolve time = 15s) ...
## Presolve removed 333703 rows and 151800 columns (presolve time = 20s) ...
## Presolve removed 333703 rows and 151800 columns (presolve time = 25s) ...
## Presolve removed 333903 rows and 151800 columns (presolve time = 32s) ...
## Presolve removed 333903 rows and 151800 columns
## Presolve time: 33.78s
## Presolved: 30303 rows, 30100 columns, 90300 nonzeros
## Variable types: 0 continuous, 30100 integer (30100 binary)
## Found heuristic solution: objective 27.0000000
## Presolved: 30303 rows, 30100 columns, 90300 nonzeros
##
##
## Root simplex log...
##
## Iteration Objective Primal Inf. Dual Inf. Time
## 0 1.0000000e+02 0.000000e+00 1.000000e+02 37s
## 129 2.0000000e+01 0.000000e+00 0.000000e+00 37s
## 129 2.0000000e+01 0.000000e+00 0.000000e+00 37s
##
## Root relaxation: objective 2.000000e+01, 129 iterations, 1.71 seconds
##
## Nodes | Current Node | Objective Bounds | Work
## Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time
##
## 0 0 20.00000 0 3 27.00000 20.00000 25.9% - 37s
## H 0 0 20.0000000 20.00000 0.00% - 37s
## 0 0 20.00000 0 3 20.00000 20.00000 0.00% - 37s
##
## Explored 1 nodes (129 simplex iterations) in 37.54 seconds
## Thread count was 2 (of 4 available processors)
##
## Solution count 2: 20 27
##
## Optimal solution found (tolerance 1.00e-01)
## Best objective 2.000000000000e+01, best bound 2.000000000000e+01, gap 0.0000%
## Warning in validityMethod(object): some species have space.held values less
## than 0, and thus are poorly represented
# show summary
summary(sim_mrs_space3)
## Run_Number Status Score Cost Planning_Units Connectivity_Total
## 1 1 OPTIMAL 20 20 20 220
## Connectivity_In Connectivity_Edge Connectivity_Out
## 1 24 160 36
## Connectivity_In_Fraction
## 1 0.1090909
# plot prioritization
plot(sim_mrs_space3, 1)
# difference between prioritizations based on unreliable and reliable formulation
plot(sim_mrs_space3, sim_mrs_space, 1, 1, main = "Difference between solutions")
An additional planning unit was selected using the reliable formulation. The prioritization based on the unreliable formulation had 21 planning units, but the prioritization based on the reliable formulation has 20 planning units. This difference occurs because the reliable formulation needs to ensure that all selected planning units with a low chance of being occupied have a suitable backup planning unit. While the reliable formulation can deliver more robust prioritizations, it takes much longer to solve conservation planning problems expressed using this formulation than the unreliable formulation. As a consequence, the reliable formulation is only feasible for particularly small problems, such as those involving few features and less than several hundred planning units.
Fragmentation is an important consideration in real-world planning situations. Up until now, we haven’t considered the effects of fragmentation on the viability of the prioritization. As a consequence, our prioritizations have tended to contain planning units without any neighbors. We can use the BLM
parameter to penalize fragmented solutions.
Let’s generate a new prioritization that heavily penalises fragmentation. Here, we will update the sim_mrs_amount
object with BLM
of 100.
# update prioritization
sim_mrs_amount_blm <- update(sim_mrs_amount, BLM = 100, Threads = threads)
## Gurobi Optimizer version 9.0.1 build v9.0.1rc0 (linux64)
## Optimize a model with 30666 rows, 30280 columns and 120720 nonzeros
## Model fingerprint: 0x20b44ac4
## Variable types: 0 continuous, 30280 integer (30280 binary)
## Coefficient statistics:
## Matrix range [7e-03, 9e+01]
## Objective range [1e+02, 4e+02]
## Bounds range [1e+00, 1e+00]
## RHS range [1e+00, 8e+02]
## Found heuristic solution: objective 4498.0000000
## Presolve removed 0 rows and 0 columns (presolve time = 7s) ...
## Presolve time: 7.76s
## Presolved: 30666 rows, 30280 columns, 120720 nonzeros
## Variable types: 0 continuous, 30280 integer (30280 binary)
## Presolved: 30666 rows, 30280 columns, 120720 nonzeros
##
##
## Root simplex log...
##
## Iteration Objective Primal Inf. Dual Inf. Time
## 0 2.2100000e+04 0.000000e+00 4.010000e+04 9s
## 1353 2.2716635e+03 0.000000e+00 8.664570e+05 10s
## 3023 7.4615023e+02 0.000000e+00 2.773993e+05 15s
## 4473 5.8606812e+02 0.000000e+00 1.247374e+05 20s
## 6303 4.6416931e+02 0.000000e+00 3.333333e+01 25s
## 6312 4.6444444e+02 0.000000e+00 0.000000e+00 25s
## 6312 4.6444444e+02 0.000000e+00 0.000000e+00 25s
##
## Root relaxation: objective 4.644444e+02, 6312 iterations, 17.51 seconds
## Total elapsed time = 38.36s
##
## Nodes | Current Node | Objective Bounds | Work
## Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time
##
## 0 0 464.44444 0 1725 4498.00000 464.44444 89.7% - 39s
## H 0 0 1759.0000000 464.44444 73.6% - 73s
## 0 0 532.32877 0 1403 1759.00000 532.32877 69.7% - 76s
## H 0 0 1548.0000000 532.32877 65.6% - 81s
## 0 0 532.32877 0 1404 1548.00000 532.32877 65.6% - 81s
## 0 0 575.37975 0 1437 1548.00000 575.37975 62.8% - 82s
## 0 0 575.37975 0 1482 1548.00000 575.37975 62.8% - 84s
## 0 0 575.37975 0 1510 1548.00000 575.37975 62.8% - 85s
## 0 0 575.37975 0 1523 1548.00000 575.37975 62.8% - 90s
## 0 0 575.37975 0 1416 1548.00000 575.37975 62.8% - 92s
## 0 0 575.37975 0 1417 1548.00000 575.37975 62.8% - 94s
## 0 0 575.37975 0 1416 1548.00000 575.37975 62.8% - 95s
## 0 0 575.37975 0 1418 1548.00000 575.37975 62.8% - 98s
## 0 0 575.37975 0 1416 1548.00000 575.37975 62.8% - 99s
## 0 0 575.37975 0 1420 1548.00000 575.37975 62.8% - 102s
## 0 0 575.37975 0 1416 1548.00000 575.37975 62.8% - 103s
## 0 0 575.37975 0 1416 1548.00000 575.37975 62.8% - 103s
## 0 2 575.37975 0 1416 1548.00000 575.37975 62.8% - 106s
## 11 14 756.03505 6 823 1548.00000 615.76724 60.2% 1976 110s
## H 13 16 1130.0000000 615.76724 45.5% 1770 111s
## H 17 20 1129.0000000 615.76724 45.5% 1578 112s
## H 24 28 920.0000000 615.76724 33.1% 1241 113s
## 40 32 851.21387 6 1240 920.00000 656.84211 28.6% 1048 115s
## 79 33 897.55102 6 890 920.00000 712.83784 22.5% 1037 120s
## 139 36 882.06897 8 506 920.00000 745.00000 19.0% 917 125s
## 208 37 cutoff 7 920.00000 782.61682 14.9% 865 130s
## 279 38 cutoff 8 920.00000 807.32394 12.2% 813 135s
##
## Cutting planes:
## Gomory: 2
## Zero half: 3
## RLT: 1
##
## Explored 334 nodes (321782 simplex iterations) in 137.56 seconds
## Thread count was 2 (of 4 available processors)
##
## Solution count 6: 920 1129 1130 ... 4498
##
## Optimal solution found (tolerance 1.00e-01)
## Best objective 9.200000000000e+02, best bound 8.350000000000e+02, gap 9.2391%
# show summary of prioritization
summary(sim_mrs_amount_blm)
## Run_Number Status Score Cost Planning_Units Connectivity_Total
## 1 1 OPTIMAL 920 20 20 220
## Connectivity_In Connectivity_Edge Connectivity_Out
## 1 31 171 18
## Connectivity_In_Fraction
## 1 0.1409091
# show amount held for each prioritization
amount.held(sim_mrs_amount_blm)
## uniform normal bimodal
## 1 0.2 0.3185057 0.3911267
# show space held for each prioritization
space.held(sim_mrs_amount_blm)
## uniform (Space 1) normal (Space 1) bimodal (Space 1)
## 1 0.6 0.6277996 0.6899822
# plot prioritization
plot(sim_mrs_amount_blm, 1)
# difference between the two prioritizations
plot(sim_mrs_amount_blm, sim_mrs_amount, 1, 1,
main = "Difference between solutions")
Here we can see that the prioritization generated using a BLM
parameter of 100 is much more clustered than the prioritization generated using a BLM
of 0. In practice, conservation planners will need to try a variety of BLM
parameters to find a suitable prioritization.
Here we will investigate how space-based targets can affect prioritizations using a more realistic dataset. We will generate prioritizations for the four bird species—blue-winged kookaburra, brown-backed honeyeater, brown falcon, pale-headed rosella—in Queensland, Australia. This region contains a broad range of different habitats—such as rainforests, woodlands, and deserts—making it ideal for this tutorial. First, we will generate a typical amount-based prioritization that aims to capture 20% of the species’ distributions. Then we will generate a prioritization that also aims to secure populations in representative parts of the species’ distributions in terms of their geographic location and their environmental heterogeneity. To do this we will generate a prioritization using 20% amount-based targets and 85% space-based targets. Finally, we will compare these prioritizations to Australia’s existing protected network.
Survey data for the species were obtained from BirdLife Australia. The survey data was rarefied using a 100 km\(^2\) grid, wherein the survey with the greatest number of repeat visits in each grid cell was chosen. To model the distribution of each species, environmental data were obtained at survey location (site). Specifically, climatic data (bio1, bio4, bio15, bio16, bio17) and classifications of the vegetation at the site were used. Occupancy-detection models were fit using Stan using manually tuned parameters (adapt deta=0.9, maximum treedepth=20, chains=4 , warmup iterations=1000, total iterations=1500) with five-fold cross-validation. In each replicate, data were partitioned into training and test sites. A full model was fit using quadratic terms for environmental variables in the site-component, and an intercept in the detection-component of the model. The full model was then subject to a step-wise backwards term deletion routine. Terms were retained when their inclusion resulted in a model with a greater area under the curve (AUC) value as calculated using the test data. Maps were generated for each species as an average of the predictions from the best model in each best replicate. To further improve the accuracy of these maps, areas well outside of the species’ known distributions were set to 0. For each species, this was achieved by masking out biogeographic regions where the species was not observed, and regions that did not have a neighboring planning unit where the species was observed. The maps were then resampled (10 km\(^2\) resolution) and cropped to the study area. The resulting maps are stored in the cs_spp
object.
# load data
data(cs_spp)
# remove areas with low occupancy (prob < 0.6)
cs_spp[cs_spp < 0.6] <- NA
# plot species' distributions
plot(cs_spp, main = c("Blue-winged kookaburra", "Brown-backed honeyeater",
"Brown falcon", "Pale-headed rosella"))
Planning units (50km\(^2\) resolution) were generated across Australia, and then clipped to the Queensland state borders and coastline. Note that we are using excessively coarse planning units so that our examples will complete relatively quickly. In a real-world planning exercise, we would use much finer planning units. To compare our prioritizations to Queensland’s existing protected area network, this network was intersected with the planning units. Planning units with more than 50% of their area inside a protected area had their status set to 2 (following conventions in Marxan). Since we do not have cost data, the prioritizations will aim to select the minimum number of planning units required to meet the targets. The planning units are stored in the cs_pu
object.
# load data
data(cs_pus)
## plot planning units
# convert SpatialPolygons to PolySet for quick plotting
cs_pus2 <- SpatialPolygons2PolySet(cs_pus)
# create vector of colours for planning units
# + light green: units not already inside reserve
# + yellow: units already inside reserve
cs_pus_cols <- rep("#c7e9c0", nrow(cs_pus@data))
cs_pus_cols[which(cs_pus$status == 2)] <- "yellow"
# set plotting window
par(mar = c(0.1, 0.1, 4.1, 0.1))
# plot polygons
PBSmapping::plotPolys(cs_pus2, col = cs_pus_cols, border = "gray30",
xlab = "", ylab = "", axes = FALSE,
main = "Case-study planning units", cex = 1.8)
To map the distribution of environmental conditions across the species’ range, 21 bioclimatic layers were obtained. These layers were cropped to Australia and subject to detrended correspondence analysis to produce two new variables. These layers are stored in the cs_space
object.
# load data
data(cs_space)
# plot variables
plot(cs_space, main = c("DC1", "DC2"), legend = FALSE, axes = FALSE)
To simplify the process of formatting data and generating prioritizations, we can use the rap
function. First, we will generate an amount-based prioritization that aims to capture 10% of the rosella’s range. We will use 50 demand points to map the geographic and environmental spaces. Be warned, the examples hereafter can take 5-10 minutes to run.
# make amount-based prioritization
# and ignore existing protected areas by discarding values in the
# status (third) column of the attribute table
cs_rs_amount <- rap(cs_pus[, -2], cs_spp, cs_space, amount.target = 0.2,
space.target = NA, n.demand.points = 50L,
include.geographic.space = TRUE, formulation = "unreliable",
kernel.method = "ks", quantile = 0.7, solve = FALSE)
## Warning in (function (pus, species, spaces = NULL, amount.target = 0.2, :
## argument to pus does not have a "status" column, setting all planning unit
## statuses to 0.
# generate prioritization
cs_rs_amount <- solve(cs_rs_amount, MIPGap = 0.1, Threads = threads)
## Gurobi Optimizer version 9.0.1 build v9.0.1rc0 (linux64)
## Optimize a model with 4 rows, 762 columns and 1336 nonzeros
## Model fingerprint: 0x99ffa403
## Variable types: 0 continuous, 762 integer (762 binary)
## Coefficient statistics:
## Matrix range [4e+02, 2e+04]
## Objective range [1e+00, 1e+00]
## Bounds range [1e+00, 1e+00]
## RHS range [1e+05, 3e+06]
## Found heuristic solution: objective 159.0000000
## Presolve time: 0.00s
## Presolved: 4 rows, 762 columns, 1336 nonzeros
## Variable types: 0 continuous, 762 integer (762 binary)
## Presolved: 4 rows, 762 columns, 1336 nonzeros
##
##
## Root relaxation: objective 1.354267e+02, 734 iterations, 0.00 seconds
##
## Nodes | Current Node | Objective Bounds | Work
## Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time
##
## 0 0 135.42671 0 4 159.00000 135.42671 14.8% - 0s
## H 0 0 136.0000000 135.42671 0.42% - 0s
## 0 0 135.42671 0 4 136.00000 135.42671 0.42% - 0s
##
## Explored 1 nodes (734 simplex iterations) in 0.01 seconds
## Thread count was 2 (of 4 available processors)
##
## Solution count 2: 136 159
##
## Optimal solution found (tolerance 1.00e-01)
## Best objective 1.360000000000e+02, best bound 1.360000000000e+02, gap 0.0000%
# show summary
summary(cs_rs_amount)
## Run_Number Status Score Cost Planning_Units Connectivity_Total
## 1 1 OPTIMAL 136 136 136 98882414
## Connectivity_In Connectivity_Edge Connectivity_Out
## 1 9936021 81420163 7526230
## Connectivity_In_Fraction
## 1 0.1004832
# plot prioritization
plot(cs_rs_amount, 1)
We can also see how well the prioritization secures the species’ distributions in the geographic and environmental attribute spaces.
# plot prioritization in geographic attribute space
p1 <- space.plot(cs_rs_amount, 1, 2, main = "Blue-winged\nkookaburra")
p2 <- space.plot(cs_rs_amount, 2, 2, main = "Brown-backed\nhoneyeater")
p3 <- space.plot(cs_rs_amount, 3, 2, main = "Brown falcon")
p4 <- space.plot(cs_rs_amount, 4, 2, main = "Pale-headed\nrosella")
gridExtra::grid.arrange(p1, p2, p3, p4, ncol = 2)
# plot prioritization in environmental attribute space
p1 <- space.plot(cs_rs_amount, 1, 1, main = "Blue-winged\nkookaburra")
p2 <- space.plot(cs_rs_amount, 2, 1, main = "Brown-backed\nhoneyeater")
p3 <- space.plot(cs_rs_amount, 3, 1, main = "Brown falcon")
p4 <- space.plot(cs_rs_amount, 4, 1, main = "Pale-headed\nrosella")
gridExtra::grid.arrange(p1, p2, p3, p4, ncol = 2)
Next, let’s generate a prioritization using amount- and space-based targets. This prioritization will secure 70% of the species distribution in geographic and environmental space.
# make amount- and space-based prioritization
cs_rs_space <- update(cs_rs_amount, space.target = 0.7, Threads = threads)
## Gurobi Optimizer version 9.0.1 build v9.0.1rc0 (linux64)
## Optimize a model with 134012 rows, 134362 columns and 535736 nonzeros
## Model fingerprint: 0xbbfab288
## Variable types: 0 continuous, 134362 integer (134362 binary)
## Coefficient statistics:
## Matrix range [5e-07, 2e+04]
## Objective range [1e+00, 1e+00]
## Bounds range [1e+00, 1e+00]
## RHS range [3e-01, 3e+06]
## Presolve removed 6758 rows and 5672 columns (presolve time = 5s) ...
## Presolve removed 6965 rows and 5672 columns (presolve time = 10s) ...
## Presolve removed 7184 rows and 5679 columns (presolve time = 15s) ...
## Presolve removed 7443 rows and 5679 columns (presolve time = 20s) ...
## Presolve removed 8731 rows and 5679 columns (presolve time = 25s) ...
## Presolve removed 9810 rows and 5679 columns (presolve time = 30s) ...
## Presolve removed 10081 rows and 5679 columns (presolve time = 35s) ...
## Presolve removed 10093 rows and 5679 columns (presolve time = 40s) ...
## Presolve removed 10267 rows and 5679 columns (presolve time = 45s) ...
## Presolve removed 10311 rows and 5679 columns (presolve time = 50s) ...
## Presolve removed 10839 rows and 5679 columns (presolve time = 55s) ...
## Presolve removed 11016 rows and 5679 columns (presolve time = 60s) ...
## Presolve removed 11065 rows and 5679 columns (presolve time = 65s) ...
## Presolve removed 12087 rows and 5679 columns (presolve time = 70s) ...
## Presolve removed 12095 rows and 5679 columns (presolve time = 75s) ...
## Presolve removed 12116 rows and 5679 columns (presolve time = 80s) ...
## Presolve removed 12116 rows and 5679 columns (presolve time = 85s) ...
## Presolve removed 12116 rows and 5800 columns (presolve time = 110s) ...
## Presolve removed 12143 rows and 5800 columns (presolve time = 120s) ...
## Presolve removed 12143 rows and 5800 columns
## Presolve time: 119.97s
## Presolved: 121869 rows, 128562 columns, 563490 nonzeros
## Variable types: 0 continuous, 128562 integer (128562 binary)
## Found heuristic solution: objective 231.0000000
## Presolve removed 368 rows and 0 columns
## Presolved: 121501 rows, 128562 columns, 561590 nonzeros
##
##
## Root simplex log...
##
## Iteration Objective Primal Inf. Dual Inf. Time
## 0 0.0000000e+00 1.314298e+02 2.195094e+10 123s
## 14426 1.3662515e+02 0.000000e+00 1.971823e+03 125s
## 35266 1.3575255e+02 0.000000e+00 2.090885e+02 130s
## 49146 1.3563479e+02 0.000000e+00 1.276275e+01 135s
## 56733 1.3562291e+02 0.000000e+00 0.000000e+00 139s
## 56733 1.3562291e+02 0.000000e+00 0.000000e+00 139s
##
## Root relaxation: objective 1.356229e+02, 56733 iterations, 18.85 seconds
## Total elapsed time = 140.86s
##
## Nodes | Current Node | Objective Bounds | Work
## Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time
##
## 0 0 135.62291 0 350 231.00000 135.62291 41.3% - 141s
## H 0 0 136.0000000 135.62291 0.28% - 142s
## 0 0 135.62291 0 350 136.00000 135.62291 0.28% - 142s
##
## Explored 1 nodes (70039 simplex iterations) in 142.13 seconds
## Thread count was 2 (of 4 available processors)
##
## Solution count 2: 136 231
##
## Optimal solution found (tolerance 1.00e-01)
## Best objective 1.360000000000e+02, best bound 1.360000000000e+02, gap 0.0000%
# show summary
summary(cs_rs_space)
## Run_Number Status Score Cost Planning_Units Connectivity_Total
## 1 1 OPTIMAL 136 136 136 98882414
## Connectivity_In Connectivity_Edge Connectivity_Out
## 1 9535693 80800559 8546162
## Connectivity_In_Fraction
## 1 0.09643467
# plot prioritization
plot(cs_rs_space, 1)
# plot prioritization in geographic attribute space
p1 <- space.plot(cs_rs_space, 1, 2, main = "Blue-winged\nkookaburra")
p2 <- space.plot(cs_rs_space, 2, 2, main = "Brown-backed\nhoneyeater")
p3 <- space.plot(cs_rs_space, 3, 2, main = "Brown falcon")
p4 <- space.plot(cs_rs_space, 4, 2, main = "Pale-headed\nrosella")
gridExtra::grid.arrange(p1, p2, p3, p4, ncol = 2)
# plot prioritization in environmental attribute space
p1 <- space.plot(cs_rs_space, 1, 1, main = "Blue-winged\nkookaburra")
p2 <- space.plot(cs_rs_space, 2, 1, main = "Brown-backed\nhoneyeater")
p3 <- space.plot(cs_rs_space, 3, 1, main = "Brown falcon")
p4 <- space.plot(cs_rs_space, 4, 1, main = "Pale-headed\nrosella")
gridExtra::grid.arrange(p1, p2, p3, p4, ncol = 2)
Let’s compare these prioritizations with Queensland’s existing protected areas system. To do this, we can create update the cs_rs_space
with manually specified solutions to create a RapSolved
object to represent the Queensland’s reserve network.
# generate vector with Australia's selections
aus_selections <- which(cs_pus$status > 0)
# create new object with Australia's network
cs_rs_aus <- update(cs_rs_amount, b = aus_selections)
Now, let’s plot the performance metrics for these prioritizations.
# define standard error function
se <- function(x) sd(x, na.rm = TRUE) / sqrt(sum(!is.na(x)))
# create a table to store the values for the 3 prioritizations
cs_results <- data.frame(
name = factor(rep(rep(c("Amount-based\nprioritization",
"Amount & space-based\nprioritization",
"Queensland reserve\nnetwork"), each = 4), 3),
levels = c(c("Amount-based\nprioritization",
"Amount & space-based\nprioritization",
"Queensland reserve\nnetwork"))),
variable = rep(c("Amount", "Geographic space", "Environmental space"),
each = 12),
species = colnames(amount.held(cs_rs_amount)),
value = c(amount.held(cs_rs_amount)[1,] , amount.held(cs_rs_space)[1, ],
amount.held(cs_rs_aus)[1, ],
space.held(cs_rs_amount, space = 2)[1, ],
space.held(cs_rs_space, space = 2)[1, ],
space.held(cs_rs_aus, space = 2)[1, ],
space.held(cs_rs_amount, space = 1)[1, ],
space.held(cs_rs_space, space = 1)[1, ],
space.held(cs_rs_aus, space = 1)[1, ])) %>%
group_by(name, variable) %>%
summarise(mean = mean(value), se = se(value))
# plot the performance metrics
ggplot(aes(x = variable, y = mean, fill = name), data = cs_results) +
geom_bar(position = position_dodge(0.9), stat = "identity") +
geom_errorbar(aes(ymin = mean - se, ymax = mean + se),
position = position_dodge(0.9),
width=0.2) +
xlab("Property of species") +
ylab("Proportion held in\nselected planning units (%)") +
scale_fill_discrete(name = "") +
theme_classic() +
theme(legend.position = "bottom", legend.direction = "horizontal",
axis.line.x = element_line(), axis.line.y = element_line())
We can see that a greater number of planning units is needed to satisfy the space-based targets. The prioritization generated using just amount-based targets has 136 planning units, and the prioritizations using amount-based and space-based targets has 136 planning units. These results suggest only a few additional planning units can result in significant improvements in the effectiveness of a prioritization.