library(ResistanceGA2)
library(terra)
library(ggplot2)
surfaces <- resistance_surfaces
if (inherits(surfaces, "PackedSpatRaster")) {
surfaces <- terra::unwrap(surfaces)
}When modeling landscape connectivity, we start with a raw environmental layer (perhaps canopy cover, elevation, percent impervious surface, or NDVI) and convert it into a resistance surface. The simplest approach would be to use the raw values directly: higher canopy cover means lower resistance for a forest-dependent species, so we could just invert the raw values. But this assumes a linear relationship between the raw covariate and the movement cost it generates, and linear relationships are rarely the right biological assumption.
Consider a forest-dependent salamander moving across a landscape of varying canopy cover. At low canopy cover (say, 10–30%), even small increases in canopy might dramatically reduce resistance: the difference between an open field and a sparse woodlot matters enormously. At high canopy cover (80–100%), the additional canopy may have little further effect; all dense forest provides similarly good habitat, and the marginal benefit of an extra 5% canopy is small. This pattern (steeply decreasing resistance at low values, leveling off at high values) is a Monomolecular relationship.
Alternatively, consider a road-crossing bird. At very low road density, roads are rare enough that they impose little resistance. At moderate road density, roads fragment the landscape and substantially impede gene flow. At very high road density (dense urban areas), the bird simply doesn’t attempt to cross; resistance is high and roughly constant. This peaked relationship (low resistance at low and high values, high resistance at intermediate values) is a Ricker pattern.
The eight transformation families in ResistanceGA2
provide a flexible vocabulary for these ecological relationships. Rather
than choosing the transformation a priori, the genetic algorithm
searches the parameter space and identifies which shape and parameter
values best explain the observed genetic differentiation.
Every continuous-surface transformation in ResistanceGA2
is defined by two parameters:
The shape parameter controls the curvature of the
response curve. It is always positive.
In ecological terms, a large shape value implies that the landscape variable acts as a near-binary switch: below a threshold, resistance is near the minimum; above the threshold, resistance is near the maximum (or vice versa). A small shape value implies a graded, continuous effect throughout the covariate range.
The max parameter sets the maximum resistance value
assigned to any cell. It is always greater than 1, because the minimum
resistance is constrained to 1.
max of 10 means the most resistant cell is 10× more
costly to cross than the least resistant cell.max of 1000 means the most resistant cell is 1000×
more costly, essentially a near-barrier.max from a broad range (default: 1 to 500 or 1000)
lets the data determine the appropriate resistance contrast.Note: The biological interpretation of
maxdepends on the distance engine used. ForcostDistance, the accumulated cost is a sum of cell values along the least-cost path, so very largemaxvalues can create effectively impassable barriers. ForcommuteDistanceand Circuitscape, very high resistance creates high pairwise resistance distances but does not create absolute barriers.
The eight families are organized into two groups: Monomolecular and Ricker, each with four variants produced by inverting (flipping the y-axis) and reversing (flipping the x-axis) the base curve. The variants allow the GA to represent all four possible “directions” of monotone and peaked responses.
We illustrate each family using the raster_orig
continuous surface with illustrative parameters
(shape = 2.5, max = 100).
Mathematical form:
resistance = max * (1 - exp(-x / shape)) + 1
The Monomolecular curve rises steeply from 1 at low values of the
covariate, then decelerates and asymptotes near max + 1. It
is the most commonly appropriate transformation for resistance modeling
because many ecological variables have diminishing-returns effects.
Biological interpretation: Resistance increases with the covariate and saturates at high values. Suitable when the landscape variable represents a cost that increases with its value but has an upper limit. Examples include:
When to use: When you expect resistance to increase with the covariate and saturate at high values. This is the default and most ecologically conservative choice.
Plot.trans(
PARM = c(2.5, 100),
Resistance = raster_orig[["cont_orig"]],
transformation = "Monomolecular",
marginal.plot = FALSE
)Monomolecular transformation: resistance increases with covariate value and saturates at high values.
Mathematical form: Same as Monomolecular, but applied to the reversed covariate values (i.e., the curve is flipped left-to-right).
Biological interpretation: Resistance decreases with the covariate and saturates at low values. Suitable when the landscape variable represents a habitat quality that increases movement ease. Examples include:
When to use: When you expect resistance to decrease with the covariate and saturate at high values of habitat quality.
Plot.trans(
PARM = c(2.5, 100),
Resistance = raster_orig[["cont_orig"]],
transformation = "Reverse Monomolecular",
marginal.plot = FALSE
)Reverse Monomolecular: resistance decreases with covariate value and saturates at high values.
Biological interpretation: Resistance decreases with the covariate but does so rapidly at low values and then levels off at high values. Similar to Reverse Monomolecular, but the approach is from a high-resistance baseline rather than a high-resistance peak. Suitable when:
When to use: When a small increase in covariate value rapidly reduces resistance, but additional increases have little further effect.
Plot.trans(
PARM = c(2.5, 100),
Resistance = raster_orig[["cont_orig"]],
transformation = "Inverse Monomolecular",
marginal.plot = FALSE
)Inverse Monomolecular: resistance decreases rapidly at low covariate values, then stabilizes.
Biological interpretation: Resistance increases with the covariate, doing so rapidly at high values and leveling off at low values. This is the mirror image of the Inverse Monomolecular. Suitable when:
When to use: When resistance increases sharply only at high covariate values, with a near-flat response at low values.
Plot.trans(
PARM = c(2.5, 100),
Resistance = raster_orig[["cont_orig"]],
transformation = "Inverse-Reverse Monomolecular",
marginal.plot = FALSE
)Inverse-Reverse Monomolecular: resistance remains low at low covariate values and rises sharply at high values.
The Ricker family produces peaked (unimodal) curves. For standard Ricker, resistance is highest at intermediate values of the covariate and lowest at the extremes; the inverse variants produce the opposite pattern (lowest at intermediate, highest at extremes). They were adapted from the Ricker stock-recruitment model in fisheries science.
Caution: Ricker transformations are more flexible than Monomolecular families and can fit a wider variety of curve shapes, including near-monotone shapes when shape parameters are extreme. This flexibility comes at the cost of increased overfitting risk. Use Ricker families only when you have a biological hypothesis that predicts a peaked resistance response.
Mathematical form:
resistance = max * x * exp(-x / shape) + 1
This creates a bell-shaped curve with a peak at intermediate covariate values. The peak location is at x = shape (larger shape values shift the peak toward higher covariate values).
Biological interpretation: Resistance is highest at intermediate covariate values, making those areas difficult to traverse. Movement is easier at the low and high extremes of the covariate range. Examples include:
When to use: When you have a clear biological hypothesis predicting that intermediate covariate values represent the most difficult-to-traverse landscape, not simply because the Ricker curve provides a better statistical fit.
Plot.trans(
PARM = c(2.5, 100),
Resistance = raster_orig[["cont_orig"]],
transformation = "Ricker",
marginal.plot = FALSE
)Ricker transformation: resistance peaks at intermediate covariate values.
Biological interpretation: Like Ricker, this produces a peaked resistance curve where intermediate values have highest resistance. The difference is that the peak is shifted toward higher covariate values compared to Ricker. This represents a scenario where intermediate-to-high covariate values are most difficult to traverse.
Examples include:
When to use: When biological reasoning predicts a peaked resistance response centered toward the higher end of the covariate range, rather than the lower-intermediate peak produced by Ricker.
Plot.trans(
PARM = c(2.5, 100),
Resistance = raster_orig[["cont_orig"]],
transformation = "Reverse Ricker",
marginal.plot = FALSE
)Reverse Ricker: resistance is lowest at intermediate covariate values.
Biological interpretation: The inverse of Ricker, producing a trough (U-shaped curve) where resistance is lowest at intermediate covariate values and highest at the extremes. This represents scenarios where intermediate conditions facilitate movement while extreme conditions resist it.
Examples include:
When to use: When your biological hypothesis predicts that intermediate covariate values represent the easiest-to-traverse landscape, with resistance increasing toward the extremes.
Plot.trans(
PARM = c(2.5, 100),
Resistance = raster_orig[["cont_orig"]],
transformation = "Inverse Ricker",
marginal.plot = FALSE
)Inverse Ricker: peaked resistance response with inverted scaling.
Biological interpretation: Like Inverse Ricker, this produces a U-shaped trough where intermediate covariate values are easiest to traverse. The difference is that the trough is shifted toward higher covariate values, representing low resistance in intermediate-to-high regions.
Example: A forest specialist that moves readily through moderate-to-high canopy cover but struggles in open areas and in extremely dense, impenetrable forest.
When to use: When biological reasoning predicts a troughed (U-shaped) resistance curve centered toward the higher end of the covariate range. This is one of the rarer ecological scenarios and should only be used with a clear hypothesis.
Plot.trans(
PARM = c(2.5, 100),
Resistance = raster_orig[["cont_orig"]],
transformation = "Inverse-Reverse Ricker",
marginal.plot = FALSE
)Inverse-Reverse Ricker: complex peaked response with inverted and reversed scaling.
The figure below shows all eight transformation families side by side
using the same illustrative parameters (shape = 2.5,
max = 100) applied to the range of the
cont_orig surface. The purpose is to show the geometric
diversity of the available curves, not to suggest these parameters are
optimal for any real analysis.
ggplot(curve_df, aes(x = original, y = transformed)) +
geom_line(linewidth = 0.9) +
facet_wrap(~ Transformation, ncol = 2, scales = "free_y") +
theme_bw() +
labs(
x = "Original covariate value",
y = "Transformed resistance"
) +
theme(
strip.text = element_text(face = "bold", size = 8),
axis.text = element_text(size = 8),
axis.title = element_text(size = 10)
)All eight transformation families at shape=2.5, max=100. Top row: Monomolecular family. Bottom row: Ricker family. Within each row: base, reverse, inverse, and inverse-reverse variants.
Key observations from this comparison:
In addition to the eight transformation families,
ResistanceGA2 includes a Distance (flat)
transformation. This assigns a constant resistance of 1 to every cell,
making the effective distance equivalent to Euclidean distance (for
costDistance) or a random-walk equivalent.
The Distance transformation serves as a model for Isolation
by Distance (IBD): genetic differentiation increases with
geographic distance, with no heterogeneous landscape influence.
Including it in SS_optim() (via
dist_mod = TRUE) provides an important baseline. If
geographic distance explains genetic patterns as well as any resistance
surface, there is no evidence for landscape-mediated connectivity.
# The Distance transformation is fit automatically when dist_mod = TRUE in SS_optim()
ss_out <- SS_optim(
gdist.inputs = gdist_inputs,
GA.inputs = ga_inputs,
dist_mod = TRUE, # adds flat-distance IBD model
null_mod = TRUE # adds intercept-only null model
)Default: Monomolecular ("M")
For most ecological variables and most study systems, one of the four Monomolecular families is the appropriate choice. Monomolecular curves:
Unless you have a specific biological hypothesis that predicts a peaked resistance response, restrict the search to Monomolecular families:
# For a single continuous layer: Monomolecular only
ga_inputs <- GA.prep(
raster = raster_orig,
Results.dir = file.path(tempdir(), "rga2-mono"),
select.trans = list("M")
)
# For two continuous layers: Monomolecular only for both
ga_inputs_2 <- GA.prep(
raster = terra::subset(surfaces, c("continuous", "feature")),
Results.dir = file.path(tempdir(), "rga2-mono-2"),
select.trans = list("M", "M")
)When Ricker Makes Sense
Consider including Ricker families ("R" or
"A" for all) when:
# Include all 8 families for one layer
ga_inputs_all <- GA.prep(
raster = raster_orig,
Results.dir = file.path(tempdir(), "rga2-all"),
select.trans = list("A") # "A" = all 8 families
)
# Monomolecular for one layer, all families for another
ga_inputs_mixed <- GA.prep(
raster = terra::subset(surfaces, c("continuous", "feature")),
Results.dir = file.path(tempdir(), "rga2-mixed"),
select.trans = list("M", "A")
)select.trans ArgumentThe select.trans argument in GA.prep() is a
list with one element per surface layer. Each element specifies which
transformation families to include in the search for that layer:
| Value | Families included |
|---|---|
"M" |
All four Monomolecular variants |
"R" |
All four Ricker variants |
"A" |
All eight families (Monomolecular + Ricker) |
NA |
No transformation (categorical surface: class-specific values) |
The default behavior detects layer type automatically and applies
"M" for continuous layers and NA for
categorical layers. You can inspect what was selected:
ga_inputs_check <- GA.prep(
raster = terra::subset(surfaces, c("categorical", "continuous")),
Results.dir = file.path(tempdir(), "rga2-check"),
monitor = FALSE,
quiet = TRUE
)
ga_inputs_check$select.trans
#> [[1]]
#> [1] NA
#>
#> [[2]]
#> [1] 1 3 5 7 9
ga_inputs_check$surface.type
#> [1] "cat" "cont"Resistance.tran() applies a specific transformation with
given parameters to a raster, returning the transformed
SpatRaster. This is useful for:
mlpe_data() and mlpe().# Monomolecular transformation
r_mono <- Resistance.tran(
transformation = "Monomolecular",
shape = 3,
max = 150,
r = raster_orig[["cont_orig"]]
)
# Ricker transformation
r_rick <- Resistance.tran(
transformation = "Ricker",
shape = 2,
max = 100,
r = raster_orig[["cont_orig"]]
)
# Reverse Monomolecular (resistance decreases with covariate)
r_revmono <- Resistance.tran(
transformation = "Reverse Monomolecular",
shape = 2,
max = 200,
r = raster_orig[["cont_orig"]]
)
# Compare visually
compare_stack <- c(r_mono, r_rick, r_revmono)
names(compare_stack) <- c("Monomolecular", "Ricker", "Rev. Monomolecular")
plot(compare_stack)Plot.trans() shows the transformation curve alongside a
marginal histogram of the original raster values, making it easy to see
how the transformation maps the covariate distribution to the resistance
scale.
Plot.trans(
PARM = c(2, 100),
Resistance = raster_orig[["cont_orig"]],
transformation = "Monomolecular",
marginal.plot = FALSE
)Monomolecular (shape=2, max=100) showing the transformation curve and the distribution of original values.
During optimization, the genetic algorithm encodes each candidate solution as a vector of parameters. For a single continuous surface, this vector contains:
select.trans).shape parameter (searched over a range, typically
0.5–15).max parameter (searched over a range, typically
1–500 or 1–1000).For categorical surfaces, the vector contains one resistance value per class (searched over a range, typically 1–2500 per class).
For multi-surface models, the vector concatenates the parameter blocks for all surfaces plus a weight vector.
The GA does not evaluate all points on a grid. Instead, it maintains a population of candidate vectors, evaluates fitness (log-likelihood), and uses crossover and mutation to move toward high-fitness regions. This makes it effective at finding good solutions in high-dimensional continuous parameter spaces without exhaustive enumeration.
Note: Because the GA is stochastic, results can vary slightly across runs. This is expected and not a sign of failure; it reflects natural variation in exploration paths. Consistent results across replicate runs indicate that the optimization has converged to a stable region.
During optimization, if a Ricker-family curve’s shape parameter
becomes very large, the peaked curve can degenerate toward a flat line,
effectively mimicking the Distance transformation.
ResistanceGA2 detects this degenerate case and collapses it
to the flat Distance solution automatically. This prevents Ricker
transformations from “cheating” by becoming effectively non-peaked while
nominally remaining in the Ricker family.
The more transformation families you include in the search, the more flexible the model and the greater the risk of fitting noise rather than signal. Key signs of potential overfitting:
Inconsistent results across replicates. If different GA runs return very different transformation families or very different parameter values, the optimization is not converging to a stable solution.
Very high max values. A
max of 2000 for a dataset where pairwise distances span a
small range may indicate the optimizer is exploiting numerical precision
rather than biology.
Ricker or Monomolecular families with extreme shape values. Very large or very small shape parameters for these curves curves can produce near-flat responses that differ little from a Distance fit but use more parameters.
Better fit than biologically plausible. If you have extremely high \(R^2\) values, scrutinize the results carefully. Be suspect of such high explanatory power unless the sampling design, genetic markers, and landscape variables are all exceptionally well-matched.
Recommendation: Always run at least two GA replicates. If the results are highly consistent, you can have more confidence in the stability of the transformation selection. If they differ substantially, consider restricting
select.transto simpler families or increasingpop.sizeandmaxiter.
| Family | Resistance Pattern | Peak/Trough Location | When to use |
|---|---|---|---|
| Monomolecular | Monotone increasing | No peak | Covariate impedes movement, effect saturates |
| Reverse Monomolecular | Monotone decreasing | No peak | Covariate facilitates movement, effect saturates |
| Inverse Monomolecular | Monotone decreasing (steep then level) | No peak | Threshold amount needed before resistance drops |
| Inverse-Reverse Monomolecular | Monotone increasing (level then steep) | No peak | Resistance rises only above a threshold value |
| Ricker | Peaked (high at intermediate) | Lower-intermediate range | Intermediate conditions are most resistant |
| Reverse Ricker | Peaked (high at intermediate) | Higher-intermediate range | High-intermediate conditions are most resistant |
| Inverse Ricker | Troughed (low at intermediate) | Lower-intermediate range | Intermediate conditions are easiest to traverse |
| Inverse-Reverse Ricker | Troughed (low at intermediate) | Higher-intermediate range | High-intermediate conditions are easiest to traverse |
| Distance | Flat (constant = 1) | None | IBD null hypothesis / geographic distance baseline |
library(ResistanceGA2)
library(terra)
# 1. Inspect the raw resistance surface.
surface <- terra::unwrap(resistance_surfaces)[["continuous"]]
terra::plot(surface)
# 2. Preview a candidate transformation before optimization.
preview_surface <- Resistance.tran(
transformation = "Monomolecular",
shape = 2,
max = 100,
r = surface
)
terra::plot(preview_surface)
# 3. Plot the response curve to see how raw values map to resistance.
Plot.trans(
PARM = c(2, 100),
Resistance = surface,
transformation = "Monomolecular",
marginal.plot = FALSE
)
# 4. Restrict GA.prep() to transformation families with biological support.
ga_inputs <- GA.prep(
raster = surface,
Results.dir = file.path(tempdir(), "rga2-transformations"),
select.trans = list(c("M", "RM"))
)How to read the output:
Resistance.tran() returns the transformed raster that would
be passed to the distance engine. Plot.trans() shows the
mapping from original raster values to optimized resistance values. Use
that curve to ask whether the transformation matches the ecological
hypothesis before letting the GA search it. A simpler
select.trans set is easier to interpret and reduces the
chance that the GA finds a flexible but biologically weak solution.
| Function | Purpose |
|---|---|
Resistance.tran() |
Apply one transformation family to a raster for a chosen parameter vector |
Plot.trans() |
Plot the transformation curve and the distribution of original raster values |
GA.prep() |
Define which transformation families and parameter ranges the GA can search |
SS_optim() |
Estimate the best transformation and parameters for each candidate surface |
MS_optim() |
Estimate transformations and weights for a multisurface resistance model |
vignette("GettingStarted", package = "ResistanceGA2"),
basic data setup and first optimization.vignette("ResistanceGA", package = "ResistanceGA2"),
full optimization and model-comparison workflow.vignette("IBE_IBR_Framework", package = "ResistanceGA2"),
using transformed resistance surfaces in conditional IBE/IBR
models.Clarke, R. T., P. Rothery, and A. F. Raybould. 2002. Confidence limits for regression relationships between distance matrices: Estimating gene flow with distance. Journal of Agricultural, Biological, and Environmental Statistics 7:361-372. https://doi.org/10.1198/108571102320
Peterman, W. E. 2018. ResistanceGA: An R package for the optimization of resistance surfaces using genetic algorithms. Methods in Ecology and Evolution 9:1638-1647. https://doi.org/10.1111/2041-210X.12984
Scrucca, L. 2013. GA: A package for genetic algorithms in R. Journal of Statistical Software 53:1–37.
Beninde, J., J. Wittische, and A. C. Frantz. 2024. Quantifying uncertainty in inferences of landscape genetic resistance due to choice of individual-based genetic distance metric. Molecular Ecology Resources 24:e13831.