| Title: | Fast Gradient-Based Optimization of Resistance Surfaces |
|---|---|
| Description: | Maximum likelihood estimation of landscape conductance surfaces from genetic data on raster graphs, using Newton and quasi-Newton optimization with analytic derivatives. Fits log-linear conductance models with optional joint estimation of Gaussian smoothing scales and spline response shapes, linked to pairwise genetic distances through maximum likelihood population effects models or to allele-frequency covariance through a Wishart likelihood on site contrasts, with the same pairwise distance and environment covariates available in both. Provides exact sparse and iterative solvers for large rasters, fixed-domain spatial cross-validation for comparing conductance formulas, and simulation tools for study design. Works with 'terra' objects. Fitting one parameterized conductance surface to genetic data, rather than regressing on separate resistance distances, follows Peterman and Pope (2021) <doi:10.1111/mec.15716>. Background on population-effects models and circuit-based covariance models is provided by Clarke, Rothery and Raybould (2002) <doi:10.1198/108571102320> and Hanks and Hooten (2013) <doi:10.1080/01621459.2012.724647>, respectively. |
| Authors: | Bill Peterman [aut, cre] (ORCID: <https://orcid.org/0000-0001-5229-9268>), Nathaniel S. Pope [aut] (ORCID: <https://orcid.org/0000-0001-8409-7812>) |
| Maintainer: | Bill Peterman <[email protected]> |
| License: | BSD_3_clause + file LICENSE |
| Version: | 1.1.0 |
| Built: | 2026-11-12 16:48:17 UTC |
| Source: | https://github.com/wpeterman/terradish |
Creates a model-selection table from fitted terradish models using
AIC, AICc, or BIC.
aic_table( mod_list, AICc = FALSE, BIC = FALSE, mod_names = NULL, verbose = FALSE )aic_table( mod_list, AICc = FALSE, BIC = FALSE, mod_names = NULL, verbose = FALSE )
mod_list |
List of fitted |
AICc |
Should second-order AIC (Akaike's information criterion with
small-sample correction) be used instead of AIC? When |
BIC |
Should BIC be used instead of AIC? When |
mod_names |
Optional model names. By default the right-hand side of
each fitted formula is used. For MLPE measurement models with additional
pairwise covariates, the default appends |
verbose |
Should the table be printed to the console? |
At nominal Wishart information, information criteria can favor extensions
that are unsupported across replicate histories. Use
terradish_cv_folds to select conductance terms and
terradish_rescale_nu to assess inferential sensitivity.
Information-criterion comparison requires the same observed response,
focal sites, graph domain, and likelihood family. Wishart fits
must also use the same effective degrees of freedom, nu.
leastsquares and mlpe are Gaussian likelihoods for the same
pairwise-distance response and can be ranked when those conditions hold.
generalized_wishart and wishart_covariance use different
response representations and must not be ranked together. Cross-family
rankings, such as mlpe versus generalized_wishart, are invalid.
The function checks response values, model family, fitted dimensions,
recorded nu, and retained graph signatures. Older fits without
signatures require the user to verify the graph domain.
A data frame containing model ranks, parameter counts, information
criterion values, delta values, weights, cumulative weights, and
log-likelihoods. The converged column is true only when both the
outer optimizer and final nuisance optimization record code 0. Missing
convergence diagnostics in older objects are treated as unverified (false);
boundary marks active parameter bounds or no resistance structure.
A warning identifies tables containing nonconverged or boundary fits.
See vignette("model-comparison", package = "terradish") for
compatible model rankings, coefficient plots, and predictive comparisons.
library(terra) data(melip) melip.altitude <- terra::unwrap(melip.altitude) melip.forestcover <- terra::unwrap(melip.forestcover) melip.coords <- terra::unwrap(melip.coords) keep <- 1:10 melip.Fst_small <- melip.Fst[keep, keep] # Coarsen the rasters so this example remains quick on CRAN. covariates <- terra::aggregate(c(melip.altitude, melip.forestcover), fact = 3, na.rm = TRUE) covariates <- scale_covariates(covariates) names(covariates) <- c("altitude", "forestcover") surface_small <- conductance_surface(covariates, melip.coords[keep], directions = 8) fit1 <- terradish(melip.Fst_small ~ altitude, surface_small, loglinear_conductance, leastsquares, control = NewtonRaphsonControl(maxit = 2, verbose = FALSE)) fit2 <- terradish(melip.Fst_small ~ altitude + forestcover, surface_small, loglinear_conductance, leastsquares, control = NewtonRaphsonControl(maxit = 2, verbose = FALSE)) aic_table(list(fit1, fit2)) aic_table(list(fit1, fit2), AICc = TRUE)library(terra) data(melip) melip.altitude <- terra::unwrap(melip.altitude) melip.forestcover <- terra::unwrap(melip.forestcover) melip.coords <- terra::unwrap(melip.coords) keep <- 1:10 melip.Fst_small <- melip.Fst[keep, keep] # Coarsen the rasters so this example remains quick on CRAN. covariates <- terra::aggregate(c(melip.altitude, melip.forestcover), fact = 3, na.rm = TRUE) covariates <- scale_covariates(covariates) names(covariates) <- c("altitude", "forestcover") surface_small <- conductance_surface(covariates, melip.coords[keep], directions = 8) fit1 <- terradish(melip.Fst_small ~ altitude, surface_small, loglinear_conductance, leastsquares, control = NewtonRaphsonControl(maxit = 2, verbose = FALSE)) fit2 <- terradish(melip.Fst_small ~ altitude + forestcover, surface_small, loglinear_conductance, leastsquares, control = NewtonRaphsonControl(maxit = 2, verbose = FALSE)) aic_table(list(fit1, fit2)) aic_table(list(fit1, fit2), AICc = TRUE)
Tuning parameters for an objective-only Armijo line search. This line search
is currently intended for optimizer = "bfgs" fits where trial
objective evaluations are cheaper than repeated full objective-plus-gradient
evaluations during Hager-Zhang line search.
ArmijoControl( initial = 1, contraction = 0.5, sufficient_decrease = 1e-04, maxit = 25L, min_alpha = sqrt(.Machine$double.eps), verbose = FALSE )ArmijoControl( initial = 1, contraction = 0.5, sufficient_decrease = 1e-04, maxit = 25L, min_alpha = sqrt(.Machine$double.eps), verbose = FALSE )
initial |
Initial trial step length. |
contraction |
Multiplicative shrinkage applied after rejected trial steps. Must be strictly between 0 and 1. |
sufficient_decrease |
Armijo sufficient-decrease constant. |
maxit |
Maximum number of backtracking steps. |
min_alpha |
Smallest step length to try before stopping. |
verbose |
Should line-search progress be printed? |
ArmijoControl() is opt-in. The default optimizer control continues to
use HagerZhangControl. Armijo trial steps only evaluate the
objective, so rejected steps avoid the gradient/backpropagation work required
by Hager-Zhang. After a step is accepted, BFGS still evaluates the full
gradient once at the accepted point so the quasi-Newton update remains
well-defined.
A list of class "terradish_armijo_control" holding the
validated Armijo line-search settings (type, initial,
contraction, sufficient_decrease, maxit,
min_alpha, and verbose), suitable for the ls.control
element of a NewtonRaphsonControl object.
ctrl <- NewtonRaphsonControl( maxit = 25, verbose = FALSE, ls.control = ArmijoControl() ) str(ctrl$ls.control)ctrl <- NewtonRaphsonControl( maxit = 25, verbose = FALSE, ls.control = ArmijoControl() ) str(ctrl$ls.control)
Tests whether a matrix supplied as the response to
generalized_wishart has squared-Euclidean geometry. The check
evaluates the positive semidefiniteness of the centered Gram matrix implied
by the observed response. It does not assess whether the chosen genetic
summary is biologically appropriate for a particular study.
check_distance_response(S, tol = 1e-07)check_distance_response(S, tol = 1e-07)
S |
Numeric square matrix. Entries are interpreted as the
squared-distance response |
tol |
Positive numeric scalar. Relative tolerance used for structural
checks and for classifying negative eigenvalues. An eigenvalue is treated
as numerical zero when it is no smaller than
|
For sites, the function forms
The response is admissible when is positive semidefinite within the
requested tolerance. Symmetry, nonnegative entries, and a zero diagonal are
necessary but are not sufficient.
Passing this geometry check does not create a Wishart sampling model.
Use covariance-derived squared distances with a defensible effective
information parameter. ratio estimators have no Wishart
degrees of freedom merely because their distance matrix passes the check;
consider mlpe for those responses.
This function checks the matrix that you actually intend to analyze because scaling, transformations, locus aggregation, and missing-data handling can change its geometry. It never applies Cailliez, Lingoes, nearest-Euclidean, or other corrections. Those procedures alter the observed response and may alter its biological interpretation.
For an independent check with ade4, note that
ade4::is.euclid() expects ordinary distances and squares them
internally. For an already squared response S, the corresponding call
is ade4::is.euclid(as.dist(sqrt(S))), not
ade4::is.euclid(as.dist(S)).
A list with the following elements:
Logical. TRUE when the centered Gram matrix is
positive semidefinite within tol.
Smallest eigenvalue of the centered Gram matrix, on
the squared-distance scale of S.
The smallest eigenvalue divided by the largest eigenvalue. Values close to zero from below usually reflect numerical rounding; materially negative values indicate incompatibility.
Number of eigenvalues below the scaled negative tolerance.
The relative tolerance supplied in tol.
The absolute negative-eigenvalue magnitude used
for the decision, equal to tol * max_eigenvalue.
Largest eigenvalue of the centered Gram matrix.
Number of rows and columns in S.
generalized_wishart,
wishart_covariance, mlpe
# Squared Euclidean distances from known coordinates are admissible. xy <- matrix(c(0, 0, 1, 0, 0, 1, 1, 1), ncol = 2, byrow = TRUE) D <- as.matrix(dist(xy))^2 check_distance_response(D) # Symmetry and a zero diagonal alone are not sufficient. bad <- matrix(c(0, 1, 1, 1, 0, 9, 1, 9, 0), nrow = 3, byrow = TRUE) check_distance_response(bad)$admissible# Squared Euclidean distances from known coordinates are admissible. xy <- matrix(c(0, 0, 1, 0, 0, 1, 1, 1), ncol = 2, byrow = TRUE) D <- as.matrix(dist(xy))^2 check_distance_response(D) # Symmetry and a zero diagonal alone are not sufficient. bad <- matrix(c(0, 1, 1, 1, 0, 9, 1, 9, 0), nrow = 3, byrow = TRUE) check_distance_response(bad)$admissible
Extracts per-cell conductance values and pointwise confidence intervals from
a fitted terradish model, returning either a raster (when the graph
stores its stack) or a matrix.
conductance(x, ...) ## S3 method for class 'terradish_graph' conductance( x, fit, quantile = 0.95, support = c("none", "focal"), support_probs = c(0, 1), clamp_covariates = NULL, ... )conductance(x, ...) ## S3 method for class 'terradish_graph' conductance( x, fit, quantile = 0.95, support = c("none", "focal"), support_probs = c(0, 1), clamp_covariates = NULL, ... )
x |
A |
... |
Additional arguments passed to methods. |
fit |
A fitted model returned by |
quantile |
Confidence level for the pointwise conductance interval.
The default |
support |
Optional support constraint for conductance evaluation.
Use |
support_probs |
Optional length-2 probability vector used with
|
clamp_covariates |
Optional character vector of covariate names to
clamp when |
Confidence intervals are computed on the log-conductance scale and then
exponentiated, so the point estimate always lies within the interval. The
intervals are asymptotic (delta method) and reflect only uncertainty in the
conductance parameters theta; they do not account for uncertainty in
the nuisance parameters phi.
With support = "focal", selected covariates are clamped to focal-site
support before conductance is evaluated. This constrains prediction to
empirical covariate support among x$demes and can stabilize extreme
tails when focal sampling is sparse.
A no-structure fit leaves conductance coefficients unidentified and cannot produce a fitted conductance surface. Other nuisance-parameter boundaries do not by themselves prevent prediction.
On a new graph, prediction reuses the fitted log-linear terms, spline knots
and centers, or Gaussian smoothing parameters and post-smoothing scaling.
It does not estimate new centers or scales on the prediction landscape.
If the original inputs were scaled before fitting, use
scale_covariates(new_rasters, reference = original_scaled_rasters)
before constructing the new graph. Other conductance factories are not
supported for prediction to a new graph.
If x$stack is non-NULL (the default when
saveStack = TRUE in conductance_surface), a three-layer
terra::SpatRaster with layer names:
estPoint-estimate conductance at each active cell.
lower<Q>Lower bound of the quantile*100% interval
(e.g. lower95 for the default quantile = 0.95).
upper<Q>Upper bound of the interval.
Otherwise, a numeric matrix with one row per active cell and columns
est, lower<Q>, and upper<Q>.
terradish, conductance_surface,
plot.terradish
library(terra) data(melip) melip.altitude <- terra::unwrap(melip.altitude) melip.forestcover <- terra::unwrap(melip.forestcover) melip.coords <- terra::unwrap(melip.coords) covariates <- c(terra::scale(melip.altitude), terra::scale(melip.forestcover)) names(covariates) <- c("altitude", "forestcover") surface <- conductance_surface(covariates, melip.coords, directions = 8) fit <- terradish(melip.Fst ~ forestcover + altitude, surface, loglinear_conductance, leastsquares, control = NewtonRaphsonControl(maxit = 5)) cond <- conductance(surface, fit) names(cond) # "est", "lower95", "upper95" plot(cond)library(terra) data(melip) melip.altitude <- terra::unwrap(melip.altitude) melip.forestcover <- terra::unwrap(melip.forestcover) melip.coords <- terra::unwrap(melip.coords) covariates <- c(terra::scale(melip.altitude), terra::scale(melip.forestcover)) names(covariates) <- c("altitude", "forestcover") surface <- conductance_surface(covariates, melip.coords, directions = 8) fit <- terradish(melip.Fst ~ forestcover + altitude, surface, loglinear_conductance, leastsquares, control = NewtonRaphsonControl(maxit = 5)) cond <- conductance(surface, fit) names(cond) # "est", "lower95", "upper95" plot(cond)
Reads spatial covariates and focal-point coordinates and builds a weighted
graph suitable for computing conductance-weighted resistance distances.
This is the first step of every terradish workflow.
conductance_surface( covariates, coords, directions = 8, saveStack = TRUE, crop_buffer = NULL )conductance_surface( covariates, coords, directions = 8, saveStack = TRUE, crop_buffer = NULL )
covariates |
A |
coords |
Locations of the focal sampling sites, in the same coordinate
reference system as |
directions |
Adjacency rule for the graph. |
saveStack |
Logical. If |
crop_buffer |
Optional nonnegative map-unit buffer to crop
|
Missing values: NA is propagated uniformly across all
layers so every cell is either complete or entirely missing. A warning is
issued if some cells are NA in only a subset of layers, because this
indicates that different covariates cover different areas; model comparison
across raster stacks with different NA patterns is problematic since
the graph vertices differ. Always compare models using the same
terradish_graph object.
Disconnected components: after masking, the graph may contain cells that are isolated from the main component. The function retains only the largest connected component and warns about the number of pruned cells. The function aborts if any focal site belongs to a disconnected component.
Cropping: when crop_buffer is supplied, cropping changes
the graph domain. Always use the same terradish_graph for all
models you intend to compare. A buffer that is too tight may cut off
landscape corridors that affect resistance distances; check stability by
refitting with several buffer values.
Categorical covariates: raster layers representing categorical
variables must be stored as factor-valued SpatRaster layers (see
as.factor and the example below). Level names are
taken from the VALUE column of the levels table when present;
otherwise the first non-ID column is used. Factor levels are
dummy-coded by model.matrix during model fitting, with
one level as the reference.
Directions: directions = 4 gives a more conservative graph
that can underestimate resistance in diagonal corridors.
directions = 8 is preferred unless there is a specific reason to
restrict movement to the cardinal directions.
Eight-neighbor adjacency is the default. Each edge has conductance
; diagonal edges receive no additional distance weighting.
Duplicate focal cells trigger a warning. Focal sites on pruned disconnected
components cannot be fitted on the retained graph.
Cropping removes possible movement paths and can inflate resistance. The supplied release audit measured increases of 15–23 percent with a two-cell buffer and 1–2 percent with ten cells. These are case-specific results; compare a larger-buffer fit before interpreting a cropped landscape.
An object of class "terradish_graph" (also inheriting
"radish_graph") containing:
demesInteger indices of cells corresponding to focal sites.
xData frame of covariate values at non-missing cells.
adjEdge list (upper-triangular, 0-based).
edge_pairsInteger matrix of adjacent cell pairs.
vertex_coordinatesxy-coordinate matrix of active cells.
directionsThe adjacency rule (4 or 8).
covariatesNames of the covariate columns.
stackThe masked SpatRaster (or NULL if
saveStack = FALSE).
library(terra) data(melip) melip.altitude <- terra::unwrap(melip.altitude) melip.forestcover <- terra::unwrap(melip.forestcover) melip.coords <- terra::unwrap(melip.coords) covariates <- c(terra::scale(melip.altitude), terra::scale(melip.forestcover)) names(covariates) <- c("altitude", "forestcover") surface <- conductance_surface(covariates, melip.coords, directions = 8) # For large rasters where sites occupy only part of the map, crop first. surface_cropped <- conductance_surface( covariates, melip.coords, directions = 8, crop_buffer = 0.05 ) # categorical covariates: # raster layers of categorical covariates should be factor-valued, see 'details' forestcover_class <- cut(terra::values(melip.forestcover)[,1], breaks = c(0, 1/6, 1/3, 1)) melip.forestcover_cat <- terra::setValues(melip.forestcover, as.numeric(forestcover_class)) melip.forestcover_cat <- terra::as.factor(melip.forestcover_cat) RAT <- levels(melip.forestcover_cat)[[1]] RAT$VALUE <- levels(forestcover_class) # explicitly define level names levels(melip.forestcover_cat) <- RAT covariates_cat <- c(melip.forestcover_cat, melip.altitude) names(covariates_cat) <- c("forestcover", "altitude")library(terra) data(melip) melip.altitude <- terra::unwrap(melip.altitude) melip.forestcover <- terra::unwrap(melip.forestcover) melip.coords <- terra::unwrap(melip.coords) covariates <- c(terra::scale(melip.altitude), terra::scale(melip.forestcover)) names(covariates) <- c("altitude", "forestcover") surface <- conductance_surface(covariates, melip.coords, directions = 8) # For large rasters where sites occupy only part of the map, crop first. surface_cropped <- conductance_surface( covariates, melip.coords, directions = 8, crop_buffer = 0.05 ) # categorical covariates: # raster layers of categorical covariates should be factor-valued, see 'details' forestcover_class <- cut(terra::values(melip.forestcover)[,1], breaks = c(0, 1/6, 1/3, 1)) melip.forestcover_cat <- terra::setValues(melip.forestcover, as.numeric(forestcover_class)) melip.forestcover_cat <- terra::as.factor(melip.forestcover_cat) RAT <- levels(melip.forestcover_cat)[[1]] RAT$VALUE <- levels(forestcover_class) # explicitly define level names levels(melip.forestcover_cat) <- RAT covariates_cat <- c(melip.forestcover_cat, melip.altitude) names(covariates_cat) <- c("forestcover", "altitude")
Runs a fast simulation study for covariance-response terradish fits.
Candidate sampling designs are created by subsetting the focal sites in an
existing conductance_surface object, responses are generated
with simulate_covariance_response, and one or more candidate
conductance models are refit to each simulated covariance matrix.
Gaussian smoothing widths are simulated and reported in map units.
covariance_response_power( theta, formula, data, sample_sizes, strategies = c("spacefill", "random"), conductance_model = loglinear_conductance, fit_models = NULL, tau = 1, sigma = 0, nu, nsim = 20L, n_designs = 1L, seed = NULL, theta_scale = c("external", "internal"), effect_parameters = NULL, alpha = 0.05, conductance_cor_threshold = 0.9, optimizer = c("auto", "newton", "bfgs"), control = NewtonRaphsonControl(maxit = 8, verbose = FALSE), solver = c("auto", "direct", "amg"), solver_control = NULL, approximation = c("none", "landmark", "coarse_raster"), approximation_control = NULL, nonnegative = TRUE, leverage = FALSE, cores = 1L, verbose = FALSE, nu_fit = nu )covariance_response_power( theta, formula, data, sample_sizes, strategies = c("spacefill", "random"), conductance_model = loglinear_conductance, fit_models = NULL, tau = 1, sigma = 0, nu, nsim = 20L, n_designs = 1L, seed = NULL, theta_scale = c("external", "internal"), effect_parameters = NULL, alpha = 0.05, conductance_cor_threshold = 0.9, optimizer = c("auto", "newton", "bfgs"), control = NewtonRaphsonControl(maxit = 8, verbose = FALSE), solver = c("auto", "direct", "amg"), solver_control = NULL, approximation = c("none", "landmark", "coarse_raster"), approximation_control = NULL, nonnegative = TRUE, leverage = FALSE, cores = 1L, verbose = FALSE, nu_fit = nu )
theta |
True conductance parameters. By default these are interpreted on
the same external scale returned by |
formula |
Formula for the true conductance model. The left hand side, if supplied, is ignored. |
data |
A |
sample_sizes |
Integer vector of focal sample sizes to assess. |
strategies |
Sampling strategies. Supported values are
|
conductance_model |
Conductance-model factory used to simulate the true response. |
fit_models |
Optional named list of candidate models to refit. Each
element may contain |
tau |
Nonnegative scaling applied to the conductance-implied covariance matrix during simulation. |
sigma |
Nonnegative nugget variance added to the covariance diagonal during simulation. |
nu |
Effective Wishart information used to simulate the response.
Larger values reduce simulated sampling noise. The fitting value defaults
to it through |
nsim |
Number of covariance-response simulations per sampling design. |
n_designs |
Number of independent site designs per sample size for
|
seed |
Optional random seed for reproducible site and response simulation. |
theta_scale |
Is |
effect_parameters |
Optional character vector of parameters used in the all-effects recovery summary. Defaults to the nonzero true parameters. |
alpha |
Significance level for Wald-style parameter recovery summaries. |
conductance_cor_threshold |
Threshold used for the conductance-surface recovery summary. |
optimizer, control, solver, solver_control, approximation, approximation_control, nonnegative, leverage, cores
|
Defaults passed to |
verbose |
Logical; print progress messages? |
nu_fit |
Effective Wishart degrees of freedom used to fit each
simulation. Defaults to the generating |
This helper avoids forward-time genetic simulation. It assumes a proposed
conductance surface and draws covariance responses from the same Wishart
likelihood fitted by wishart_covariance. That makes it useful
for approximate power and sample-size screening before investing in slower
individual-based simulations.
Sampling designs are efficient because the raster graph is not rebuilt for
each sample size. The helper subsets data$demes and the corresponding
right-hand-side columns, so all candidate designs share the same graph,
covariate matrix, and conductance-model basis expansion.
Fits that land on the no-signal boundary are retained as non-detections in
parameter-level power summaries. Their coefficient estimates and standard
errors are reported as NA, matching explicit fitting failures.
For spline conductance models, individual basis coefficients are not usually
the most interpretable recovery target. Inspect parameter_summary for
basis-level behavior, but prefer summary$conductance_power,
summary$mean_conductance_cor, and model-selection rates when judging
whether the non-linear conductance surface was recovered.
An object of class "terradish_covariance_power" containing:
resultsOne row per fitted model and simulated response.
summaryScenario-level fit, model-selection, and conductance-recovery summaries.
parameter_resultsOne row per assessed parameter and simulation replicate.
parameter_summaryParameter-level power, bias, RMSE, and coverage summaries.
settingsSimulation settings and true parameters, including
nu for simulation and nu_fit for inference.
Power and coverage are internal to the assumed response model. Matching
simulation and fitting values cannot diagnose incorrect information in a
real data set. Nonconverged fits count as failed fits and non-detections;
both outer and final nuisance optimization must record code 0. Missing
convergence diagnostics also count as failed fits.
Inspect fit_rate before interpreting power or coverage.
data(melip) melip.altitude <- terra::unwrap(melip.altitude) melip.forestcover <- terra::unwrap(melip.forestcover) melip.coords <- terra::unwrap(melip.coords) covariates <- c(melip.altitude, melip.forestcover) names(covariates) <- c("altitude", "forestcover") covariates <- scale_covariates(covariates) # Coarsen the raster: a power sweep refits many times, so cell count is the # dominant cost. Use the full resolution for a real design study. covariates <- terra::aggregate(covariates, fact = 3, na.rm = TRUE) surface <- conductance_surface(covariates, melip.coords, directions = 8, saveStack = TRUE) spline_model <- smooth_loglinear_conductance( ~ forestcover + s(altitude, df = 4), surface$x) theta <- attr(spline_model, "default") theta["forestcover"] <- -0.15 theta[grepl("^s\\(altitude\\)", names(theta))] <- c(-0.65, 1.1, -0.9, 0.45) gaussian_model <- gaussian_smoothed_loglinear_conductance( surface, scale_vars = "altitude") power <- covariance_response_power( theta = theta, formula = ~ forestcover + s(altitude, df = 4), data = surface, sample_sizes = c(8, 12), strategies = "spacefill", conductance_model = smooth_loglinear_conductance, fit_models = list( spline = list(formula = ~ forestcover + s(altitude, df = 4), conductance_model = smooth_loglinear_conductance), gaussian = list(formula = ~ altitude + forestcover, conductance_model = gaussian_model) ), tau = 0.8, sigma = 0.12, nu = 150, nsim = 3, # raise to 200+ for a real power estimate n_designs = 1, seed = 1, control = NewtonRaphsonControl(maxit = 12, verbose = FALSE) ) power$summary power$parameter_summarydata(melip) melip.altitude <- terra::unwrap(melip.altitude) melip.forestcover <- terra::unwrap(melip.forestcover) melip.coords <- terra::unwrap(melip.coords) covariates <- c(melip.altitude, melip.forestcover) names(covariates) <- c("altitude", "forestcover") covariates <- scale_covariates(covariates) # Coarsen the raster: a power sweep refits many times, so cell count is the # dominant cost. Use the full resolution for a real design study. covariates <- terra::aggregate(covariates, fact = 3, na.rm = TRUE) surface <- conductance_surface(covariates, melip.coords, directions = 8, saveStack = TRUE) spline_model <- smooth_loglinear_conductance( ~ forestcover + s(altitude, df = 4), surface$x) theta <- attr(spline_model, "default") theta["forestcover"] <- -0.15 theta[grepl("^s\\(altitude\\)", names(theta))] <- c(-0.65, 1.1, -0.9, 0.45) gaussian_model <- gaussian_smoothed_loglinear_conductance( surface, scale_vars = "altitude") power <- covariance_response_power( theta = theta, formula = ~ forestcover + s(altitude, df = 4), data = surface, sample_sizes = c(8, 12), strategies = "spacefill", conductance_model = smooth_loglinear_conductance, fit_models = list( spline = list(formula = ~ forestcover + s(altitude, df = 4), conductance_model = smooth_loglinear_conductance), gaussian = list(formula = ~ altitude + forestcover, conductance_model = gaussian_model) ), tau = 0.8, sigma = 0.12, nu = 150, nsim = 3, # raise to 200+ for a real power estimate n_designs = 1, seed = 1, control = NewtonRaphsonControl(maxit = 12, verbose = FALSE) ) power$summary power$parameter_summary
Crops a raster stack to the bounding box of a set of focal coordinates plus a user-supplied map-unit buffer. This is a simple pre-processing helper for large landscapes where the sampled sites occupy only a small part of the raster extent.
crop_to_focal_buffer(covariates, coords, buffer)crop_to_focal_buffer(covariates, coords, buffer)
covariates |
A |
coords |
Focal coordinates accepted by |
buffer |
Nonnegative buffer distance in the raster's map units. A scalar
applies the same buffer in both x and y directions; a length-two vector is
interpreted as |
Cropping reduces the number of graph vertices before any Laplacian factorization occurs. It is most useful when sampling locations are spatially clustered inside a much larger raster. It will not help much when focal sites span the whole raster, because the cropped extent will be close to the original extent.
The requested buffer is expanded internally by half a cell width in each
direction so the cropped raster safely contains the cells holding the focal
sites even when buffer = 0. If the raster is in longitude/latitude,
the buffer is in degrees; use a projected raster for direct distance
interpretation.
conductance_surface() also accepts crop_buffer = and applies
this helper before graph construction.
Cropping can increase resistance by removing paths outside the retained
extent. The supplied release audit found increases of 15–23 percent at a
two-cell buffer and 1–2 percent at ten cells. These results are specific to
that landscape. Compare fits over larger buffers as a sensitivity check.
Keep the response, focal sites, covariate scaling, and formula fixed, then
refit with successively larger buffers. Compare coefficients, confidence
intervals, and fitted resistance distances. Report the buffer and whether
increasing it changes the substantive conclusion.
A message flags buffers smaller than half the maximum intersite distance.
A cropped terra::SpatRaster. Any terradish_scale
metadata attached by scale_covariates is preserved.
library(terra) r <- terra::rast(nrows = 20, ncols = 20, vals = 1:400, xmin = 0, xmax = 20, ymin = 0, ymax = 20) pts <- matrix(c(4.5, 4.5, 6.5, 6.5, 7.5, 5.5), ncol = 2, byrow = TRUE) cropped <- crop_to_focal_buffer(r, pts, buffer = 2) cropped # The same crop can be applied automatically while building the graph. surface <- conductance_surface(r, pts, directions = 4, crop_buffer = 2) nrow(surface$x) # number of retained graph cellslibrary(terra) r <- terra::rast(nrows = 20, ncols = 20, vals = 1:400, xmin = 0, xmax = 20, ymin = 0, ymax = 20) pts <- matrix(c(4.5, 4.5, 6.5, 6.5, 7.5, 5.5), ncol = 2, byrow = TRUE) cropped <- crop_to_focal_buffer(r, pts, buffer = 2) cropped # The same crop can be applied automatically while building the graph. surface <- conductance_surface(r, pts, directions = 4, crop_buffer = 2) nrow(surface$x) # number of retained graph cells
Fixes one scale at each grid value and reoptimizes all remaining conductance and measurement parameters on the fitted graph.
gaussian_scale_profile(fit, layer, n = 25, level = 0.95)gaussian_scale_profile(fit, layer, n = 25, level = 0.95)
fit |
An unslimmed Gaussian scale fit with its original model closures. |
layer |
Name of the raster layer whose scale is profiled. |
n |
Number of grid points across the fitted scale bounds, at least five. The fitted estimate is also included if it is not already on the grid. |
level |
Confidence level between zero and one. |
The interval is the connected likelihood-ratio support region
containing the fitted estimate, using the one-degree-of-freedom chi-square
cutoff. Grid crossings are refined by root finding. Increase n to
check for additional support regions. Limits stop at the original bounds.
At a boundary the usual chi-square calibration is an approximation.
Failed optimizations stop the calculation instead of silently dropping
profile points. Both conductance and final measurement-parameter convergence
are required. This can be substantially slower than Wald intervals.
A list with layer, estimate, level, interval, grid, and fit. The grid
contains scale values and profile log likelihoods. The returned fit stores
the interval, so confint(result$fit) uses it at the matching level.
See vignette("gaussian-scale-optimization", package = "terradish")
for scale profiles, forest-effect plots, and interpretation in map units.
gaussian_scale_summary,
gaussian_smoothed_loglinear_conductance,
terradish_inference
set.seed(24) r <- terra::rast(nrows = 10, ncols = 10, xmin = 0, xmax = 10, ymin = 0, ymax = 10, crs = "EPSG:3857") terra::values(r) <- rnorm(100) names(r) <- "x" graph <- conductance_surface(r, terra::xyFromCell(r, seq(3, 98, 5)), directions = 4) factory <- gaussian_smoothed_loglinear_conductance(graph, sigma_lower = 0.4, sigma_upper = 1.5) response <- simulate_covariance_response(c(x = 0.7, sigma.x = 0.8), ~x, graph, conductance_model = factory, tau = 1, sigma = 0.1, nu = 10000, seed = 14)$covariance fit <- terradish(response ~ x, graph, conductance_model = factory, measurement_model = wishart_covariance, nu = 10000, optimizer = "newton") profile <- gaussian_scale_profile(fit, "x", n = 5) profile$interval confint(profile$fit) # The stored profile interval replaces the matching scale Wald interval.set.seed(24) r <- terra::rast(nrows = 10, ncols = 10, xmin = 0, xmax = 10, ymin = 0, ymax = 10, crs = "EPSG:3857") terra::values(r) <- rnorm(100) names(r) <- "x" graph <- conductance_surface(r, terra::xyFromCell(r, seq(3, 98, 5)), directions = 4) factory <- gaussian_smoothed_loglinear_conductance(graph, sigma_lower = 0.4, sigma_upper = 1.5) response <- simulate_covariance_response(c(x = 0.7, sigma.x = 0.8), ~x, graph, conductance_model = factory, tau = 1, sigma = 0.1, nu = 10000, seed = 14)$covariance fit <- terradish(response ~ x, graph, conductance_model = factory, measurement_model = wishart_covariance, nu = 10000, optimizer = "newton") profile <- gaussian_scale_profile(fit, "x", n = 5) profile$interval confint(profile$fit) # The stored profile interval replaces the matching scale Wald interval.
Converts fitted Gaussian sigma parameters into map-unit, cell-based,
and Gaussian-kernel interpretation summaries for a fitted model produced with
gaussian_smoothed_loglinear_conductance.
gaussian_scale_summary( object, probabilities = c(0.5, 0.95), distance_per_map_unit = NULL, distance_unit = NULL )gaussian_scale_summary( object, probabilities = c(0.5, 0.95), distance_per_map_unit = NULL, distance_unit = NULL )
object |
A fitted |
probabilities |
Probabilities used to summarize the one-dimensional half-width and two-dimensional radial extent of the Gaussian kernel. |
distance_per_map_unit |
Optional scalar conversion factor used to express
|
distance_unit |
Optional label used when
|
The returned table always reports sigma on the raster's native map
scale and in raster cell widths. When sigma conversion is enabled during
fitting, the table also reports the internal optimization scale and its
conversion factor back to map units. For each probability p,
axis_* gives the one-dimensional half-width
, while radial_* gives the isotropic
two-dimensional radius containing
proportion p of the Gaussian kernel mass. Here is
the standard normal quantile function and is the smoothing scale.
These quantities summarize the fitted raster-smoothing kernel. They are not
estimates of dispersal distance, movement distance, or home-range size.
Use summary(object)$sigma_table to inspect standard errors and
bound flags, and confint(object) for bound-truncated intervals;
smoothing scales have no
reported z or p values. Use gaussian_scale_profile when a
profile interval is needed. A positive scale can reflect dispersal
averaging, and even the half-cell lower bound applies some smoothing.
If the retained raster is in longitude/latitude, the native-unit results are
in degrees. In that case, use a projected raster for direct distance
interpretation, or provide an approximate distance_per_map_unit
conversion if you need a quick descriptive summary.
A data frame with one row per fitted sigma parameter.
covariate identifies the layer. sigma is its fitted map-unit
scale; sigma_lower and sigma_upper are fitting bounds.
sigma_internal, sigma_conversion, and
sigma_conversion_mode describe the optimization units.
native_unit labels map units or degrees, and
sigma_cells_x, sigma_cells_y express the scale in cell widths.
Probability-specific axis_* and radial_* columns give the
theoretical Gaussian distances described above, in native units and,
when requested, converted units. They describe the untruncated Gaussian;
retained raster windows can omit some of that mass.
data(melip) forestcover <- terra::unwrap(melip.forestcover) names(forestcover) <- "forestcover" # Coarsen the raster so the example runs quickly; the Gaussian model # re-smooths the layer at every optimizer step, so cost scales with cell # count. Use the full resolution in a real analysis. forestcover <- terra::aggregate(forestcover, fact = 3, na.rm = TRUE) # Feed the model the raw (unsmoothed, unstandardized) raster: it smooths and # standardizes internally at each candidate sigma. surface <- conductance_surface(forestcover, terra::unwrap(melip.coords), directions = 8, saveStack = TRUE) fit <- terradish(melip.Fst ~ forestcover, data = surface, conductance_model = gaussian_smoothed_loglinear_conductance(surface), measurement_model = leastsquares, optimizer = "bfgs") # sigma is in map units. The melip rasters are unprojected, so those units # are degrees; distance_per_map_unit rescales the reported distances. gaussian_scale_summary(fit) gaussian_scale_summary(fit, distance_per_map_unit = 111, distance_unit = "km")data(melip) forestcover <- terra::unwrap(melip.forestcover) names(forestcover) <- "forestcover" # Coarsen the raster so the example runs quickly; the Gaussian model # re-smooths the layer at every optimizer step, so cost scales with cell # count. Use the full resolution in a real analysis. forestcover <- terra::aggregate(forestcover, fact = 3, na.rm = TRUE) # Feed the model the raw (unsmoothed, unstandardized) raster: it smooths and # standardizes internally at each candidate sigma. surface <- conductance_surface(forestcover, terra::unwrap(melip.coords), directions = 8, saveStack = TRUE) fit <- terradish(melip.Fst ~ forestcover, data = surface, conductance_model = gaussian_smoothed_loglinear_conductance(surface), measurement_model = leastsquares, optimizer = "bfgs") # sigma is in map units. The melip rasters are unprojected, so those units # are degrees; distance_per_map_unit rescales the reported distances. gaussian_scale_summary(fit) gaussian_scale_summary(fit, distance_per_map_unit = 111, distance_unit = "km")
Creates a conductance-model factory that smooths selected raster layers with a native Gaussian FFT operator and treats the corresponding Gaussian scale parameters as part of the optimized conductance parameter vector.
gaussian_smoothed_loglinear_conductance( surface, scale_vars = NULL, standardize = TRUE, sigma_lower = NULL, sigma_upper = NULL, sigma_conversion = c("cell", "map"), sigma_conversion_factor = NULL )gaussian_smoothed_loglinear_conductance( surface, scale_vars = NULL, standardize = TRUE, sigma_lower = NULL, sigma_upper = NULL, sigma_conversion = c("cell", "map"), sigma_conversion_factor = NULL )
surface |
A |
scale_vars |
Optional character vector naming the raster layers whose Gaussian smoothing scales should be estimated. Defaults to all numeric raster variables present in the model formula. |
standardize |
Should each smoothed layer be centered and scaled across
the active graph cells at every parameter evaluation? Default |
sigma_lower, sigma_upper
|
Optional lower and upper bounds for the
Gaussian scale parameters on their natural distance scale. Scalar values
are recycled across all scaled rasters; named vectors may be used to set
different bounds by raster name. When omitted, |
sigma_conversion |
Internal scaling applied during optimization.
|
sigma_conversion_factor |
Optional positive scalar or named vector used to override the default sigma conversion factors. This is most useful when you want optimization to happen in user-defined distance units while still reporting fitted sigma values in map units. |
This is a scale-aware extension of loglinear_conductance.
Factory settings are evaluated when the factory is created, so factories
constructed in a loop retain their own raster choices and scale bounds.
Given a model formula such as
~ altitude * forestcover + I(altitude^2), the fitted parameter vector
contains both conductance coefficients and one natural-scale sigma
parameter for each scaled raster layer. The spatial kernel is Gaussian and is
evaluated with an FFT-based normalized convolution over the retained raster
stack from surface. The smoothing domain is fixed within each graph
evaluation, which keeps the sigma derivatives smooth during optimization.
When terradish is run with
approximation = "coarse_raster", the smoother is rebuilt on each
coarse graph while preserving the full-resolution sigma bounds and
conversion so coarse-stage starts remain interpretable in map units.
Internally, the sigma parameters may be optimized on a converted scale
(for example, approximate cell widths) and are then converted back to map
units before they are returned to users.
These parameters describe Gaussian smoothing of the supplied raster in its
map units. They are not direct estimates of dispersal distance, movement
distance, home-range size, or the scale of a causal ecological process.
Unlike the standard fixed-raster workflow, pre-scaling with
scale_covariates is unnecessary when
standardize = TRUE: the supplied raster is smoothed at each proposed
sigma, then its active-cell values are centered and scaled. Because
scale_covariates applies a positive affine transformation to each
layer, pre-scaling gives the same standardized smoothed values up to
numerical tolerance. Supplying original values keeps the preprocessing and
coefficient interpretation clear. With standardize = FALSE,
pre-scaling changes the covariate and coefficient units.
With the default standardization, a coefficient is relative log conductance
per standard deviation of the smoothed layer at the current sigma. The
layer is re-standardized at every candidate sigma during fitting. The
half-cell lower bound still smooths; near the upper bound, the smoothed
layer acts as a broad spatial trend. Dispersal averaging can favor a positive
sigma even when no separate ecological scale of effect exists.
The Gaussian kernel is truncated to a raster-sized window. The default
upper bound keeps approximately three sigma inside half the smaller raster
dimension; increasing it cannot recover kernel mass outside that window.
Inspect summary(fit)$sigma_table for bound flags and
gaussian_scale_profile for profile intervals. Scale estimates
do not receive z or p values because zero is outside the fitting interval.
Wald intervals are truncated at the bounds; a bound-limited interval does
not demonstrate a precisely identified scale.
The current implementation supports numeric raster formulas built from raw
raster names, interactions, and polynomial/arithmetic terms that can be
differentiated analytically by D after smoothing the base
raster layers. Factor-valued raster layers are rejected because Gaussian
smoothing is only defined here for numeric rasters; use an unscaled fixed
conductance model if you need categorical raster predictors.
For this conductance model, terradish prefers
optimizer = "bfgs" when optimizer = "auto", and leverage /
partial_X calculations are currently disabled because the current
leverage machinery assumes a one-to-one mapping between fitted conductance
parameters and design-matrix covariates, which does not hold once separate
Gaussian sigma parameters are introduced.
A function of class terradish_conductance_model_factory that
can be supplied as the conductance_model= argument to
terradish.
See vignette("gaussian-scale-optimization", package = "terradish")
for fitting, plotting, and interpreting smoothing scales on the pond landscape.
data(melip) melip.forestcover <- terra::unwrap(melip.forestcover) melip.coords <- terra::unwrap(melip.coords) # Use the raw raster here. The Gaussian model smooths first and then # standardizes internally. names(melip.forestcover) <- "forestcover" # Coarsened so the example runs quickly: the raster is re-smoothed at every # candidate sigma. Use the full resolution in a real analysis. melip.forestcover <- terra::aggregate(melip.forestcover, fact = 3, na.rm = TRUE) surface <- conductance_surface( melip.forestcover, melip.coords, directions = 8, saveStack = TRUE ) gaussian_model <- gaussian_smoothed_loglinear_conductance(surface) fit <- terradish( melip.Fst ~ forestcover, data = surface, conductance_model = gaussian_model, measurement_model = mlpe, optimizer = "auto", leverage = FALSE ) gaussian_scale_summary(fit) plot(fit, type = "sigma") # On a full-resolution raster, a coarse-raster warm start cuts the cost of # the scale search. With exact_refine = TRUE the reported fit is refined # back on the original graph, so the estimates stay exact. fit_coarse <- terradish( melip.Fst ~ forestcover, data = surface, conductance_model = gaussian_model, measurement_model = mlpe, approximation = "coarse_raster", approximation_control = list(factor = 2, exact_refine = TRUE), optimizer = "auto", leverage = FALSE )data(melip) melip.forestcover <- terra::unwrap(melip.forestcover) melip.coords <- terra::unwrap(melip.coords) # Use the raw raster here. The Gaussian model smooths first and then # standardizes internally. names(melip.forestcover) <- "forestcover" # Coarsened so the example runs quickly: the raster is re-smoothed at every # candidate sigma. Use the full resolution in a real analysis. melip.forestcover <- terra::aggregate(melip.forestcover, fact = 3, na.rm = TRUE) surface <- conductance_surface( melip.forestcover, melip.coords, directions = 8, saveStack = TRUE ) gaussian_model <- gaussian_smoothed_loglinear_conductance(surface) fit <- terradish( melip.Fst ~ forestcover, data = surface, conductance_model = gaussian_model, measurement_model = mlpe, optimizer = "auto", leverage = FALSE ) gaussian_scale_summary(fit) plot(fit, type = "sigma") # On a full-resolution raster, a coarse-raster warm start cuts the cost of # the scale search. With exact_refine = TRUE the reported fit is refined # back on the original graph, so the estimates stay exact. fit_coarse <- terradish( melip.Fst ~ forestcover, data = surface, conductance_model = gaussian_model, measurement_model = mlpe, approximation = "coarse_raster", approximation_control = list(factor = 2, exact_refine = TRUE), optimizer = "auto", leverage = FALSE )
A function of class "terradish_measurement_model" that evaluates the
generalized Wishart likelihood for an admissible pairwise genetic
squared-distance matrix. This model requires a distance representation
that is coherently related to a centered positive semidefinite covariance
matrix; it is not valid for an arbitrary dissimilarity matrix. The effective
marker degrees of freedom must also be supplied. For covariance-matrix data,
use wishart_covariance.
generalized_wishart( E, S, phi, nu, gradient = TRUE, hessian = TRUE, partial = TRUE, nonnegative = TRUE, validate = FALSE )generalized_wishart( E, S, phi, nu, gradient = TRUE, hessian = TRUE, partial = TRUE, nonnegative = TRUE, validate = FALSE )
E |
Conductance-implied covariance matrix: the generalized inverse of the graph Laplacian at the current conductance parameters. Passed automatically by the optimizer. |
S |
Square, symmetric admissible genetic squared-distance matrix with
zero diagonal. It must have the same dimensions as |
phi |
Named numeric vector of nuisance parameters |
nu |
One finite positive number describing effective Wishart information.
You must supply it; the fit does not estimate it. It is not the marker
count. See the effective-information section and report sensitivity across
plausible values with |
gradient |
Logical. Compute gradient of the negative log-likelihood
with respect to |
hessian |
Logical. Compute Hessian with respect to |
partial |
Logical. Compute second partial derivatives with respect to
|
nonnegative |
Unused; present for interface consistency. |
validate |
Logical. Numerically validate gradients and Hessians via numDeriv? Very slow; for debugging small examples only. |
The nuisance parameters are:
tauNonnegative scale applied to the conductance-implied
covariance E. A tau near zero signals no detectable
contribution from conductance-implied covariance under this model.
sigmaLog-scale identity component: the nugget variance
added to the diagonal is . It represents diagonal
variance not captured by the graph covariance, including sampling
variation and model mismatch. It is not a smoothing scale or a direct
estimate of genetic drift or population size.
The fitted covariance is . The model
evaluates the generalized Wishart log-likelihood for the observed
admissible squared-distance matrix S after projecting out the grand mean, as
described in McCullagh (2009).
generalized_wishart and wishart_covariance share the
same parameterization but differ in what S represents:
this function takes a squared-distance matrix; wishart_covariance
takes a covariance matrix. When the covariance and its implied
distance representation carry the same information under the same centering,
scaling, sites, and nu, the two functions return the same objective
(up to numerical tolerance) because both use the same site-contrast
likelihood and omit response-only normalizing terms. This equality does not
make AIC values from arbitrary distance and covariance fits interchangeable.
ratio estimators do not have Wishart degrees of freedom. A
passed geometry check is necessary but does not establish this sampling
likelihood. Prefer covariance-derived squared distances and document the
construction; use mlpe for ratio-estimator responses.
The function checks S with check_distance_response
before evaluating the likelihood. Substantive violations stop the fit. Tiny
structural deviations within the numerical tolerance are symmetrized and
set to zero where appropriate. No Euclidean correction is applied.
When phi is missing, a list with elements phi
(starting values c(tau = 1, sigma = 0)), lower
(c(0, -Inf)), and upper. Otherwise a list containing:
objective |
Negative log-likelihood. |
fitted |
Matrix of expected squared genetic distances. |
boundary |
Logical; |
gradient |
Gradient with respect to |
hessian |
Hessian matrix with respect to |
gradient_E |
Gradient with respect to |
partial_E |
Jacobian of |
partial_S |
Jacobian of |
jacobian_E |
Function for reverse-mode AD through |
jacobian_S |
Function for reverse-mode AD through |
Fitting uses the same small-nugget safeguards as
wishart_covariance, retaining supplied bounds and fixed
parameters. A verified numerical zero residual-variance limit is recorded
separately from a zero graph contribution; summary() omits Wald
uncertainty for its log-scale nugget coordinate.
For Wishart likelihoods, nu scales the log likelihood. Point
estimates do not depend on it, but standard errors, likelihood-ratio tests,
and information criteria do. It is a dispersion parameter that you set,
rather than the number of markers. Linkage and the population history
shared by loci can make effective information much smaller than the SNP
count. In forward-time simulations used to validate terradish, standard
errors at the SNP count were three to five times smaller than the spread
among replicate histories. Those model-based errors understated uncertainty.
Use terradish_cv_folds to compare conductance formulas, and
terradish_rescale_nu to report inference across plausible
values. For the same successful folds and nuisance settings, changing
nu rescales Wishart CV scores without changing their ranking.
Neither procedure identifies the true effective information or repairs
an unsuitable response matrix.
McCullagh P. 2009. Marginal likelihood for distance matrices. Statistica Sinica 19:631-649.
check_distance_response,
wishart_covariance, wishart_covariates,
mlpe,
terradish
library(terra) data(melip) melip.altitude <- terra::unwrap(melip.altitude) melip.forestcover <- terra::unwrap(melip.forestcover) melip.coords <- terra::unwrap(melip.coords) covariates <- c(melip.altitude, melip.forestcover) names(covariates) <- c("altitude", "forestcover") surface <- conductance_surface(covariates, melip.coords, directions = 8) # Inverse of the graph Laplacian at the null conductance model. laplacian_inv <- terradish_distance(theta = matrix(0, 1, 2), formula = ~forestcover + altitude, data = surface, terradish::loglinear_conductance, covariance = TRUE)$covariance[,,1] # Generate an admissible squared-distance response from a PSD covariance. Sigma <- 0.8 * laplacian_inv + 0.2 * diag(nrow(laplacian_inv)) observed_covariance <- rWishart(1, df = 40, Sigma = Sigma)[, , 1] / 40 observed_squared_distance <- dist_from_cov(observed_covariance) generalized_wishart(laplacian_inv, observed_squared_distance, nu = 40, phi = c(tau = 0.8, sigma = log(0.2)))library(terra) data(melip) melip.altitude <- terra::unwrap(melip.altitude) melip.forestcover <- terra::unwrap(melip.forestcover) melip.coords <- terra::unwrap(melip.coords) covariates <- c(melip.altitude, melip.forestcover) names(covariates) <- c("altitude", "forestcover") surface <- conductance_surface(covariates, melip.coords, directions = 8) # Inverse of the graph Laplacian at the null conductance model. laplacian_inv <- terradish_distance(theta = matrix(0, 1, 2), formula = ~forestcover + altitude, data = surface, terradish::loglinear_conductance, covariance = TRUE)$covariance[,,1] # Generate an admissible squared-distance response from a PSD covariance. Sigma <- 0.8 * laplacian_inv + 0.2 * diag(nrow(laplacian_inv)) observed_covariance <- rWishart(1, df = 40, Sigma = Sigma)[, , 1] / 40 observed_squared_distance <- dist_from_cov(observed_covariance) generalized_wishart(laplacian_inv, observed_squared_distance, nu = 40, phi = c(tau = 0.8, sigma = log(0.2)))
Tuning parameters for the Hager-Zhang line-search implementation.
HagerZhangControl( delta = 0.1, sigma = 0.9, alphamax = Inf, rho = 5, epsilon = 1e-06, gamma = 0.66, linesearchmax = 50, psi3 = 0.1, c = 1, verbose = FALSE )HagerZhangControl( delta = 0.1, sigma = 0.9, alphamax = Inf, rho = 5, epsilon = 1e-06, gamma = 0.66, linesearchmax = 50, psi3 = 0.1, c = 1, verbose = FALSE )
delta |
Sufficient decrease constant (Wolfe condition 1). Must satisfy
|
sigma |
Curvature condition constant (Wolfe condition 2). Must satisfy
|
alphamax |
Upper bound on the trial step length. The default |
rho |
Multiplicative expansion factor applied during the initial
bracketing phase. Must be |
epsilon |
Non-negative tolerance defining an "approximate Wolfe" regime
near a local minimum: a small positive value (default |
gamma |
Minimum fractional reduction of the bracket required after each
secant step. Steps that shrink by less than this factor trigger bisection
instead. Must be in |
linesearchmax |
Maximum number of line-search function evaluations. Raise this if the optimizer reports repeated line-search failures on difficult problems. |
psi3 |
Backtracking multiplier applied when the initial trial step
produces a non-finite objective. Must be in |
c |
Initial trial step length passed to the line search. The default
|
verbose |
Should line-search progress (trial step lengths and objective values) be printed to the console? |
HagerZhangControl() is the default line search used by
NewtonRaphsonControl. It implements the approximate Wolfe
conditions from Hager and Zhang (2006), which combine sufficient-decrease
and curvature checks with a bracketing-plus-secant strategy.
The default parameters work well for most resistance-surface optimization problems. Common reasons to change them:
Line-search failures: increase linesearchmax or
loosen sigma toward 1.
Slow convergence with many small steps: tighten sigma
(e.g. 0.1) to enforce a stronger curvature condition.
Initial non-finite objectives: increase psi3 to take
larger backtracking steps when c is too large.
A named list of class "HagerZhangControl" holding the line
search settings. The elements are the arguments above, stored unchanged:
delta, sigma, alphamax, rho, epsilon,
gamma, linesearchmax, psi3, c, and
verbose. Pass the object to the ls.control argument of
NewtonRaphsonControl rather than to terradish
directly; inspect it with str(). The list carries no fitted
quantities, so nothing in it needs back-transforming.
Hager WW, Zhang H. 2006. Algorithm 851: CG_DESCENT, a conjugate gradient
method with guaranteed descent. ACM Transactions on Mathematical Software
32(1):113-137.
NewtonRaphsonControl, ArmijoControl
ctrl <- HagerZhangControl(verbose = FALSE, linesearchmax = 10) str(ctrl) # Use inside NewtonRaphsonControl: opt_ctrl <- NewtonRaphsonControl( maxit = 50, ls.control = HagerZhangControl(sigma = 0.5, linesearchmax = 30) )ctrl <- HagerZhangControl(verbose = FALSE, linesearchmax = 10) str(ctrl) # Use inside NewtonRaphsonControl: opt_ctrl <- NewtonRaphsonControl( maxit = 50, ls.control = HagerZhangControl(sigma = 0.5, linesearchmax = 30) )
Matched SNP and microsatellite genotypes for 160 adult terrestrial salamanders in each of two simulated movement scenarios on a real southeastern Ohio landscape. The primary example demonstrates nonlinear isolation by resistance with individual sampling. The animals and drying index are hypothetical; neither species occurrence nor field-calibrated movement is claimed.
data(individual_salamanders)data(individual_salamanders)
A list with three elements:
dataTwo named scenarios, compensatory_500 and
control_500. Each contains samples, snp_dosage,
microsat_alleles, and covariance, described below.
modelsTwenty checked model records per scenario: uniform
and quadratic drying models, two marker types, and partitions 0 (all
adults) through 4 (one spatial quadrant withheld). Names combine marker,
model, and partition, for example SNP2500__IBR__0. Records contain
log-conductance coefficients theta, graph covariance multiplier
phi$tau on its natural nonnegative scale, log nugget variance
phi$sigma, starting-point diagnostics, convergence, curvature,
covariance score, RMSE, and predicted-mean range. These are plain lists,
not fitted terradish objects.
metadataFixed drying center center, linear divisor
sd_linear, and quadratic divisor sd_quadratic; the latter
is SD of squared deviations, not squared SD. start_halfwidth
defines additional starting points, not coefficient bounds.
epsg, native_resolution_m, graph_resolution_m,
directions, extent, core, and graph_nodes
describe the spatial grid (EPSG:26917, meters). sample_size,
main_SNPs, and matched_microsatellites give panel sizes.
objective, biological_scope, and
raw_marker_selection state interpretation and selection rules.
Within each scenario, samples has 160 rows in genotype order.
sample_id and sample_key identify the animal;
id, mom, and dad are recorded ecological pedigree
identifiers. stratum gives one of 16 sampling strata;
quadrant and fold identify the four spatial validation blocks.
age is in years, sex is 0 for female and 1 for male,
x_local and y_local are meters from the domain origin, and
easting and northing are projected UTM coordinates.
drying is the interpolated hypothetical index from 0 to 1.
snp_dosage contains nested integer matrices named 500,
1000, and 2500. Entries 0, 1, and 2 count one allele.
Locus names identify synthetic genomic blocks and within-block positions.
microsat_alleles is a 160 by 48 integer matrix; adjacent columns hold
the two repeat-count alleles at each of 24 loci. All marker row names match
samples$sample_id. Microsatellite allele labels are categorical,
not continuous response values.
covariance contains centered matrices named SNP2500 and
microsat24, averaging unstandardized allele-frequency inner products
across loci. Their implied squared distances range from 0 to 2. This is
different from the locus-frequency weighting in
cov_from_biallelic and is not a Dps response.
Each scenario follows 500 years, with maximum age 20 for both sexes. The compensatory scenario allows faster, straighter movement at intermediate drying but greater danger in very dry terrain. The control holds behavioral drying at 0.5 while retaining forest settlement and demography. It is a control for drying-dependent movement, not every possible resistance effect. Samples occupy distinct 10 m cells, are at least 10 m apart, and exclude direct or shared recorded parents and grandparents. Distant kin can remain.
These analyses use wishart_covariance with nu = 1 as a
covariance quasi-likelihood, not a literal independent-Gaussian-locus
sampling model. Nominal Hessian uncertainty, AIC, and significance tests
are not used as genetic-sampling evidence. Two hundred genomic-block
resamples and a second genomic assay check conditional marker variation;
neither supplies independent ecological replication. Spatial folds were
reused during workflow development. Marker discovery conditions on the
full sampled panel. The matched microsatellites retain mixed predictive
evidence. No separate causal isolation-by-environment effect was simulated.
Installed files in extdata/individual-salamanders provide the 10 m
drying terms and environment, native 30 m terrain, all 40 original prediction
records, comparative scores, the independent assay, and provenance. The
10 m grid interpolates native 30 m information; it adds no new measurements.
The vignette explains these files and the longer opt-in reproduction.
vignette("data-generation", package = "terradish"),
vignette("individual-salamanders", package = "terradish").
Simulated genotypes from recorded individual pedigrees on terrain
derived from USGS 3DEP elevation and 2023 Annual NLCD Collection 1.0.
See system.file("extdata", "individual-salamanders", "README.md",
package = "terradish") for data sources, processing, and limitations.
cov_from_genetic_data,
cov_from_biallelic, dist_from_cov,
vignette("individual-salamanders", package = "terradish").
data(individual_salamanders) animals <- individual_salamanders$data$compensatory_500 G <- animals$snp_dosage[["2500"]] S <- cov_from_genetic_data(G, scale = FALSE, normalize = "none") / (2 * ncol(G)) dim(S) # 160 individual sampling locations range(as.matrix(dist_from_cov(S))) # squared allele-frequency distance table(animals$samples$fold) # 40 withheld adults per spatial folddata(individual_salamanders) animals <- individual_salamanders$data$compensatory_500 G <- animals$snp_dosage[["2500"]] S <- cov_from_genetic_data(G, scale = FALSE, normalize = "none") / (2 * ncol(G)) dim(S) # 160 individual sampling locations range(as.matrix(dist_from_cov(S))) # squared allele-frequency distance table(animals$samples$fold) # 40 withheld adults per spatial fold
A function of class "terradish_measurement_model" that evaluates a
Gaussian likelihood with independent errors and a linear mean structure
relating observed genetic distances to resistance distances. This is the
fastest measurement model and a useful exploratory or diagnostic choice.
For distance-response inference, mlpe represents shared-site
dependence that this model omits.
leastsquares( E, S, phi, nu = NULL, gradient = TRUE, hessian = TRUE, partial = TRUE, nonnegative = TRUE, validate = FALSE )leastsquares( E, S, phi, nu = NULL, gradient = TRUE, hessian = TRUE, partial = TRUE, nonnegative = TRUE, validate = FALSE )
E |
Conductance-implied covariance matrix: the generalized inverse of the graph Laplacian, evaluated at the current conductance parameters. Passed automatically by the optimizer; users normally do not call this function directly. |
S |
Square, symmetric matrix of observed pairwise genetic distances
(e.g. |
phi |
Named numeric vector of nuisance parameters |
nu |
Unused; present for a common interface with Wishart measurement models. |
gradient |
Logical. Compute gradient of the negative log-likelihood
with respect to |
hessian |
Logical. Compute Hessian of the negative log-likelihood with
respect to |
partial |
Logical. Compute second partial derivatives with respect to
|
nonnegative |
Logical. Constrain the IBR slope |
validate |
Logical. Numerically validate gradients and Hessians via numDeriv? Very slow; intended for debugging small examples only. |
The nuisance parameters are:
alphaIntercept of the regression of genetic distance on resistance distance.
betaIBR slope; constrained when
nonnegative = TRUE.
tauLog-precision of the Gaussian errors: residual variance
is .
The mean structure is , where
is the resistance distance between sites and
(derived from E) and are independent Gaussian errors
with common precision .
Pairwise genetic distances are not independent: any two pairs sharing a
sampling site can be correlated. leastsquares ignores this dependence,
so its standard errors can be too small. Use it for fast exploration,
diagnostics, or initial parameter searches rather than as the default basis
for distance-response inference.
When phi is missing, a list with elements phi
(starting values), lower, and upper (parameter bounds).
Otherwise a list containing:
objective |
Negative log-likelihood. |
fitted |
Matrix of expected genetic distances (same dimensions as |
boundary |
Logical; |
gradient |
Gradient of the negative log-likelihood with respect to |
hessian |
Hessian matrix with respect to |
gradient_E |
Gradient with respect to |
partial_E |
Jacobian of |
partial_S |
Jacobian of |
jacobian_E |
Function for reverse-mode algorithmic differentiation through |
jacobian_S |
Function for reverse-mode algorithmic differentiation through |
mlpe, generalized_wishart,
wishart_covariance, terradish
library(terra) data(melip) melip.altitude <- terra::unwrap(melip.altitude) melip.forestcover <- terra::unwrap(melip.forestcover) melip.coords <- terra::unwrap(melip.coords) covariates <- c(melip.altitude, melip.forestcover) names(covariates) <- c("altitude", "forestcover") surface <- conductance_surface(covariates, melip.coords, directions = 8) # inverse of graph Laplacian at null model (IBD) laplacian_inv <- terradish_distance(theta = matrix(0, 1, 2), formula = ~forestcover + altitude, data = surface, terradish::loglinear_conductance, covariance = TRUE)$covariance[,,1] leastsquares(laplacian_inv, melip.Fst) #without 'phi': return MLE of phi leastsquares(laplacian_inv, melip.Fst, phi = c(0., 0.5, -0.1))library(terra) data(melip) melip.altitude <- terra::unwrap(melip.altitude) melip.forestcover <- terra::unwrap(melip.forestcover) melip.coords <- terra::unwrap(melip.coords) covariates <- c(melip.altitude, melip.forestcover) names(covariates) <- c("altitude", "forestcover") surface <- conductance_surface(covariates, melip.coords, directions = 8) # inverse of graph Laplacian at null model (IBD) laplacian_inv <- terradish_distance(theta = matrix(0, 1, 2), formula = ~forestcover + altitude, data = surface, terradish::loglinear_conductance, covariance = TRUE)$covariance[,,1] leastsquares(laplacian_inv, melip.Fst) #without 'phi': return MLE of phi leastsquares(laplacian_inv, melip.Fst, phi = c(0., 0.5, -0.1))
Returns a function of class "terradish_conductance_model" that
represents a log-linear mapping from spatial covariates to conductance.
This is the recommended conductance model for most applications.
loglinear_conductance(formula, x)loglinear_conductance(formula, x)
formula |
Model formula describing which spatial covariates drive
conductance. The left-hand side is ignored; only the right-hand side terms
are used (e.g. |
x |
Data frame of spatial covariates extracted from a
|
The conductance at grid cell i is:
where is the value of covariate at cell and
is the corresponding conductance parameter.
The intercept is intentionally omitted: multiplying all conductances by a common factor rescales resistance inversely, and the measurement model's resistance coefficient absorbs that factor. The absolute conductance level is therefore not identifiable.
Interpreting :
: higher values of covariate increase
fitted relative conductance and tend to lower graph resistance distance,
conditional on the other terms and graph domain.
: higher values lower fitted relative conductance.
: the fitted conductance is locally unchanged by
that term, conditional on the model.
A one-standard-deviation increase in covariate multiplies
fitted conductance by when the covariate was
standardized and the term is linear.
These are model-based associations, not direct estimates of movement rate, habitat suitability, or a causal landscape effect.
The exponential link guarantees strictly positive conductances for any real
.
Categorical covariates must be stored as factor columns in x
(see conductance_surface for how to encode them). They are
dummy-coded using the default R contrasts via
model.matrix, with one level dropped as a reference.
In-formula transformations such as I(x^2) and interaction terms
x * z are supported.
A function of class "terradish_conductance_model" that
accepts a numeric vector of conductance parameters theta and
returns a list with elements conductance (a vector of per-cell
conductance values), confint (a function for confidence intervals),
and derivative functions used internally by the optimizer.
gaussian_smoothed_loglinear_conductance,
conductance_surface, terradish
x <- data.frame(altitude = c(-1, 0, 1), forestcover = c(0.2, 0.6, 0.5)) model <- loglinear_conductance(~ altitude + forestcover, x) # Evaluate at specific parameter values fit <- model(c(altitude = 0.3, forestcover = -0.2)) fit$conductance # per-cell conductance values # Interaction and polynomial terms work too x2 <- data.frame(altitude = c(-1, -0.25, 0.5, 1), fc = c(0.2, 0.6, 0.5, 0.8)) model2 <- loglinear_conductance(~ altitude + I(altitude^2) + fc, x2)x <- data.frame(altitude = c(-1, 0, 1), forestcover = c(0.2, 0.6, 0.5)) model <- loglinear_conductance(~ altitude + forestcover, x) # Evaluate at specific parameter values fit <- model(c(altitude = 0.3, forestcover = -0.2)) fit$conductance # per-cell conductance values # Interaction and polynomial terms work too x2 <- data.frame(altitude = c(-1, -0.25, 0.5, 1), fc = c(0.2, 0.6, 0.5, 0.8)) model2 <- loglinear_conductance(~ altitude + I(altitude^2) + fc, x2)
Returns the entries strictly below the main diagonal of a square matrix, scanned column-by-column (standard R column-major order).
lower(x)lower(x)
x |
A square numeric matrix. |
This is a thin wrapper around x[lower.tri(x)]. It is provided as a
convenience for extracting vectorized pairwise measurements from symmetric
matrices such as distance or covariance matrices.
A numeric vector of length containing the strict
lower-triangular entries of x, where is the dimension of
x.
m <- matrix(1:9, nrow = 3) lower(m) # entries (2,1), (3,1), (3,2) # Useful for extracting pairwise distances: D <- matrix(c(0, 0.1, 0.3, 0.1, 0, 0.2, 0.3, 0.2, 0), nrow = 3) lower(D)m <- matrix(1:9, nrow = 3) lower(m) # entries (2,1), (3,1), (3,2) # Useful for extracting pairwise distances: D <- matrix(c(0, 0.1, 0.3, 0.1, 0, 0.2, 0.3, 0.2, 0), nrow = 3) lower(D)
Landscape and genetic data for 37 sampled populations of the stingless bee
genus Melipona in Brazil, bundled with terradish so every help
page and vignette can run a complete worked example. The four objects are
stored separately in the package data, so data(melip) loads all of
them into the calling environment at once.
data(melip)data(melip)
Four objects, loaded together by data(melip):
melip.altitudeA wrapped terra::SpatRaster,
156 rows by 180 columns at 0.045 degree resolution, covering roughly
42.9 to 34.8 degrees west and 9.5 to 2.5 degrees south in unprojected
longitude/latitude. Values are altitude already rescaled to roughly the
unit interval (range 0 to 1.06), not meters. Of the 28,080 cells,
22,443 are land and 5,637 are NA (ocean), and only the non-NA
cells enter the conductance graph.
melip.forestcoverA wrapped terra::SpatRaster on the
same grid, giving forest cover as a proportion (range 0.004 to 0.948).
melip.coordsA wrapped terra::SpatVector of 37 point
locations, one per sampled population, in the same longitude/latitude
coordinate system as the rasters.
melip.FstA symmetric 37 by 37 numeric matrix of pairwise
among the same 37 populations, with a zero diagonal and no
dimnames. Values range from -0.062 to 0.280. Four off-diagonal entries
are slightly negative, which is a normal outcome of unbiased
estimators when true differentiation is near zero.
Gaussian distance models such as mlpe can retain these
estimates. Their presence is one reason not to treat this matrix as
automatically admissible for generalized_wishart.
The two rasters are stored as terra::wrap()ped objects because a live
SpatRaster holds an external pointer that cannot be serialized into a
package. Call terra::unwrap() on each one before use; the same
applies to the SpatVector of coordinates.
The rasters are unprojected, so distances are in degrees rather than meters.
That matters for any function whose output carries a distance unit: the
sigma reported by gaussian_scale_summary, for instance,
is in degrees for this data set. Reproject before interpreting the fitted
Gaussian raster-smoothing scale as a distance.
The altitude layer arrives pre-rescaled, but the two layers are still on
different scales, so run scale_covariates before fitting, as
every example here does.
Redistributed from the radish package, where the data were
assembled for the original resistance-surface optimizer. A primary
citation for the underlying Melipona sampling and genotyping is not
recorded in the package sources.
conductance_surface for turning these layers into a
graph, and terradish for fitting a model to them. See
vignette("mlpe-pairwise-distance", package = "terradish") for the
full worked example with these data.
data(melip) # rasters and points are wrapped for storage; unwrap before use altitude <- terra::unwrap(melip.altitude) forestcover <- terra::unwrap(melip.forestcover) coords <- terra::unwrap(melip.coords) altitude # 156 x 180 grid, values on ~[0, 1] nrow(coords) # 37 sampled populations dim(melip.Fst) # 37 x 37 pairwise Fst range(melip.Fst) # note the small negative values # MLPE can retain the negative estimates in this pairwise-distance response Fst <- melip.Fst round(Fst[1:4, 1:4], 3) terra::plot(altitude, main = "Altitude (rescaled)") terra::points(coords, pch = 19, cex = 0.7, col = "red")data(melip) # rasters and points are wrapped for storage; unwrap before use altitude <- terra::unwrap(melip.altitude) forestcover <- terra::unwrap(melip.forestcover) coords <- terra::unwrap(melip.coords) altitude # 156 x 180 grid, values on ~[0, 1] nrow(coords) # 37 sampled populations dim(melip.Fst) # 37 x 37 pairwise Fst range(melip.Fst) # note the small negative values # MLPE can retain the negative estimates in this pairwise-distance response Fst <- melip.Fst round(Fst[1:4, 1:4], 3) terra::plot(altitude, main = "Altitude (rescaled)") terra::points(coords, pch = 19, cex = 0.7, col = "red")
A function of class "terradish_measurement_model" that evaluates the
maximum likelihood population effects (MLPE) likelihood. MLPE accounts for
the non-independence of pairwise genetic distances that share a sampling
site. It is the preferred distance-response likelihood when a defensible
effective Wishart degrees-of-freedom value is unavailable or the response is
not admissible for generalized_wishart.
mlpe( E, S, phi, nu = NULL, gradient = TRUE, hessian = TRUE, partial = TRUE, nonnegative = TRUE, validate = FALSE )mlpe( E, S, phi, nu = NULL, gradient = TRUE, hessian = TRUE, partial = TRUE, nonnegative = TRUE, validate = FALSE )
E |
Conductance-implied covariance matrix: the generalized inverse of the graph Laplacian, evaluated at the current conductance parameters. Passed automatically by the optimizer; users normally do not call this function directly. |
S |
Square, symmetric matrix of observed pairwise genetic distances
(e.g. |
phi |
Named numeric vector of nuisance parameters |
nu |
Unused; present for a common interface with Wishart measurement models. |
gradient |
Logical. Compute gradient of the negative log-likelihood
with respect to |
hessian |
Logical. Compute Hessian of the negative log-likelihood with
respect to |
partial |
Logical. Compute second partial derivatives with respect to
|
nonnegative |
Logical. Constrain the IBR slope |
validate |
Logical. Numerically validate gradients and Hessians via numDeriv? Very slow; for debugging small examples only. |
The likelihood uses maximum likelihood (ML), not restricted maximum likelihood (REML). AIC can therefore compare different conductance formulas fitted to the same response, sites, graph, and likelihood family. That comparison does not validate the correlation model: in package validation, model-based standard errors understated replicate variation about 1.7-fold. Use spatial CV to assess out-of-sample support for conductance terms.
With the standard nuisance bounds, fitting profiles the regression coefficients and residual precision analytically at each shared-site correlation, then searches the correlation before Newton refinement. The resistance predictor is centered and scaled internally so changing its units does not change the nuisance optimum or convergence criterion. Stored coefficients remain in the original units. Fixed predictive parameters and custom nuisance bounds use their supplied constraints. A profile reaching the upper numerical correlation endpoint is rejected because a regular finite nuisance optimum has not been established.
summary() reports rho on its internal logit scale, with a
Wald interval on that scale. Convert an estimate or interval endpoint with
plogis(rho) / 2 to obtain the shared-site correlation, which lies
between 0 and 0.5. A value near 0.5 means site identity explains most of
the residual variation. When the correlation approaches zero, the logit
parameter is poorly identified, so summary() reports rho as 0
with no standard error and prints a boundary note. This boundary differs
from a zero resistance coefficient, which leaves conductance unidentified.
To read the other nuisance parameters, alpha is the expected
genetic distance at zero resistance distance, beta is the increase
in genetic distance per unit of resistance distance, and the residual
standard deviation is exp(-tau / 2).
The nuisance parameters are:
alphaIntercept of the mean structure.
betaResistance-distance (IBR) slope; constrained
when nonnegative = TRUE.
tauLog-precision parameter: residual variance is
.
rhoUnconstrained optimization-scale parameter. The actual
shared-site correlation is
.
The mean structure is , where
is the resistance distance derived from E. The
residual vector follows the MLPE correlation structure of Clarke
et al. (2002): two pairs and sharing site
have correlation , while pairs sharing no site are
uncorrelated.
Use mlpe_covariates to extend the mean structure with
additional fixed pairwise covariates (isolation by environment).
Coefficients describe conditional associations under the specified mean and
correlation structure; they do not isolate causal environmental effects.
When the fitted internal correlation logit is below -8,
summary() reports the shared-site correlation at zero and omits its
Wald uncertainty. The parameter remains in the degrees of freedom because
it was estimated; fixing it in advance would define a different model.
When phi is missing, a list with elements phi
(starting values), lower, and upper (parameter bounds).
Otherwise a list containing:
objective |
Negative log-likelihood. |
fitted |
Matrix of expected genetic distances (same dimensions as |
boundary |
Logical; |
gradient |
Gradient with respect to |
hessian |
Hessian matrix with respect to |
gradient_E |
Gradient with respect to |
partial_E |
Jacobian of |
partial_S |
Jacobian of |
jacobian_E |
Function for reverse-mode AD through |
jacobian_S |
Function for reverse-mode AD through |
vignette("mlpe-pairwise-distance", package = "terradish") fits and
interprets MLPE models for a pairwise matrix.
Clarke RT, Rothery P, Raybould AF. 2002. Confidence limits for regression relationships between distance matrices: estimating gene flow with distance. Journal of Agricultural, Biological, and Environmental Statistics 7(3):361-372.
leastsquares, mlpe_covariates,
generalized_wishart, terradish
library(terra) data(melip) melip.altitude <- terra::unwrap(melip.altitude) melip.forestcover <- terra::unwrap(melip.forestcover) melip.coords <- terra::unwrap(melip.coords) covariates <- c(melip.altitude, melip.forestcover) names(covariates) <- c("altitude", "forestcover") surface <- conductance_surface(covariates, melip.coords, directions = 8) # inverse of graph Laplacian at null model (IBD) laplacian_inv <- terradish_distance(theta = matrix(0, 1, 2), formula = ~forestcover + altitude, data = surface, terradish::loglinear_conductance, covariance = TRUE)$covariance[,,1] mlpe(laplacian_inv, melip.Fst) #without 'phi': return MLE of phi # Set the actual shared-site correlation to 0.2 with qlogis(2 * 0.2) mlpe(laplacian_inv, melip.Fst, phi = c(0, 0.5, -0.1, qlogis(2 * 0.2)))library(terra) data(melip) melip.altitude <- terra::unwrap(melip.altitude) melip.forestcover <- terra::unwrap(melip.forestcover) melip.coords <- terra::unwrap(melip.coords) covariates <- c(melip.altitude, melip.forestcover) names(covariates) <- c("altitude", "forestcover") surface <- conductance_surface(covariates, melip.coords, directions = 8) # inverse of graph Laplacian at null model (IBD) laplacian_inv <- terradish_distance(theta = matrix(0, 1, 2), formula = ~forestcover + altitude, data = surface, terradish::loglinear_conductance, covariance = TRUE)$covariance[,,1] mlpe(laplacian_inv, melip.Fst) #without 'phi': return MLE of phi # Set the actual shared-site correlation to 0.2 with qlogis(2 * 0.2) mlpe(laplacian_inv, melip.Fst, phi = c(0, 0.5, -0.1, qlogis(2 * 0.2)))
Creates an MLPE measurement model whose mean structure includes both an IBR term (resistance distance) and one or more fixed pairwise environmental covariates (IBE terms).
mlpe_covariates( x, coords = NULL, transform = c("absdiff", "sqdiff", "euclidean", "manhattan"), scale = FALSE, rho_start = 0.2 )mlpe_covariates( x, coords = NULL, transform = c("absdiff", "sqdiff", "euclidean", "manhattan"), scale = FALSE, rho_start = 0.2 )
x |
Pairwise endpoint-difference covariates (the output of
|
coords |
Optional focal-point coordinates, required when |
transform |
Pairwise transform applied when |
scale |
Logical. Standardize site-level covariates before the
pairwise transform? Passed to |
rho_start |
Starting value for the MLPE correlation parameter |
The fitted mean structure is:
where is the resistance distance (IBR) and is the
row of pairwise endpoint-difference covariates for the pair
(IBE). The residuals follow the MLPE correlation structure of Clarke et al.
(2002).
The coefficients are conditional on resistance distance, the
other pairwise covariates, and the MLPE correlation model. They describe
associations with the genetic-distance response. They do not cleanly
separate IBE from IBR, establish causality, or identify a unique ecological
mechanism when spatial predictors are correlated.
The nuisance parameters (intercept , IBR slope ,
IBE coefficients , log-precision tau, and the
unconstrained correlation parameter rho) are all profiled out during
optimization; only the conductance parameters in the formula are optimized
by the outer loop. The actual MLPE shared-site correlation is
.
When x is site-level data or the output of
pairwise_endpoint_covariates(), the pairwise covariate matrix is
stored as an attribute of the returned function. This allows
terradish_cv_folds to rebuild the correct pairwise covariates for
each train/test split automatically.
Unclassed matrices are interpreted as pairwise columns and must
have rows. Use a data frame for site-level columns, or
pairwise_endpoint_covariates to construct their differences.
pairwise_covariates combines these with other pairwise inputs.
A function of class "terradish_measurement_model" suitable
for use as the measurement_model argument of terradish,
terradish_grid, and terradish_cv_folds.
See vignette("ibe-ibr-workflow", package = "terradish") for
paired environmental differences, genetic-response contrasts, and their
distinction from covariance kernels.
Construct one pairwise_covariates object to use the same
environmental transforms and geographic distances in both likelihood
families. Their fitted conductance surfaces are conditional on these terms,
and conductance signs can change when the mean structure changes. Report
fits with and without pairwise effects. A uniform ~ 1 conductance
model with pairwise terms represents IBD plus IBE, not IBE alone.
A positive environmental coefficient means differentiation increases with
environmental difference conditional on resistance. It does not distinguish
assortative mating, dispersal filtering, local adaptation, or site variance
that rises at environmental extremes. MLPE coefficients are unconstrained;
Wishart kernel weights are nonnegative. Compare their effects in
resistance-distance units with terradish_ibe_ratio, which
divides by the resistance coefficient and propagates joint uncertainty.
Ratios are undefined when that denominator is zero.
Clarke RT, Rothery P, Raybould AF. 2002. Confidence limits for regression relationships between distance matrices: estimating gene flow with distance. Journal of Agricultural, Biological, and Environmental Statistics 7(3):361-372.
mlpe, pairwise_endpoint_covariates,
terradish_cv_folds
library(terra) data(melip) melip.altitude <- terra::unwrap(melip.altitude) melip.forestcover <- terra::unwrap(melip.forestcover) melip.coords <- terra::unwrap(melip.coords) covariates <- c(terra::scale(melip.altitude), terra::scale(melip.forestcover)) names(covariates) <- c("altitude", "forestcover") surface <- conductance_surface(covariates, melip.coords, directions = 8) g_joint <- mlpe_covariates(melip.altitude, melip.coords, transform = "absdiff", scale = TRUE) fit_joint <- terradish(melip.Fst ~ altitude + forestcover, surface, terradish::loglinear_conductance, g_joint) summary(fit_joint)library(terra) data(melip) melip.altitude <- terra::unwrap(melip.altitude) melip.forestcover <- terra::unwrap(melip.forestcover) melip.coords <- terra::unwrap(melip.coords) covariates <- c(terra::scale(melip.altitude), terra::scale(melip.forestcover)) names(covariates) <- c("altitude", "forestcover") surface <- conductance_surface(covariates, melip.coords, directions = 8) g_joint <- mlpe_covariates(melip.altitude, melip.coords, transform = "absdiff", scale = TRUE) fit_joint <- terradish(melip.Fst ~ altitude + forestcover, surface, terradish::loglinear_conductance, g_joint) summary(fit_joint)
Computes the expected change in the genetic-distance response associated
with changing one or more pairwise covariates in an
mlpe_covariates measurement model. This is useful for
interpreting IBE coefficients, which are profiled nuisance parameters rather
than conductance-surface coefficients.
mlpe_response_change( object, covariate = NULL, probs = c(0.1, 0.9), values = NULL, conf.level = 0.95 )mlpe_response_change( object, covariate = NULL, probs = c(0.1, 0.9), values = NULL, conf.level = 0.95 )
object |
A fitted |
covariate |
Optional character vector naming the pairwise covariates to summarize. Defaults to all pairwise covariates stored in the measurement model. |
probs |
Numeric vector of length two giving the lower and upper
quantiles used to define the contrast when |
values |
Optional numeric vector of length two giving explicit lower
and upper covariate values for a single |
conf.level |
Confidence level for the Wald confidence interval. |
For a model fitted with mlpe_covariates(), the pairwise mean structure
includes terms of the form . This function reports
, the expected response-scale change in
genetic distance across the requested covariate contrast, conditional on the
fitted conductance surface.
The interval uses the conditional standard error for the MLPE covariate
coefficient from summary(object) and treats the selected covariate
contrast as fixed.
The reported contrast is a conditional association under the fitted MLPE
model. It is not a causal effect or proof that IBE has been separated from
IBR or other spatially correlated predictors.
A data frame with one row per covariate and columns describing the low and high covariate values, the contrast, estimated response-scale change, confidence limits, and confidence level.
vignette("ibe-ibr-workflow", package = "terradish").
mlpe_covariates,
pairwise_endpoint_covariates, terradish
data(melip) altitude <- terra::unwrap(melip.altitude) forestcover <- terra::unwrap(melip.forestcover) coords <- terra::unwrap(melip.coords) covariates <- c(altitude, forestcover) names(covariates) <- c("altitude", "forestcover") covariates <- scale_covariates(terra::aggregate(covariates, fact = 3, na.rm = TRUE)) surface <- conductance_surface(covariates, coords, directions = 8) # site-level altitude, turned into a pairwise |difference| IBE predictor site_altitude <- data.frame(altitude = terra::extract(altitude, coords)[, 2]) g_joint <- mlpe_covariates(site_altitude) # joint isolation by environment and isolation by resistance fit_joint <- terradish(melip.Fst ~ altitude + forestcover, data = surface, conductance_model = loglinear_conductance, measurement_model = g_joint) # Expected change in genetic distance across the covariate's 10th-to-90th # percentile spread, on the response scale rather than the log scale. change <- mlpe_response_change(fit_joint) change # A narrower, interquartile contrast mlpe_response_change(fit_joint, probs = c(0.25, 0.75))data(melip) altitude <- terra::unwrap(melip.altitude) forestcover <- terra::unwrap(melip.forestcover) coords <- terra::unwrap(melip.coords) covariates <- c(altitude, forestcover) names(covariates) <- c("altitude", "forestcover") covariates <- scale_covariates(terra::aggregate(covariates, fact = 3, na.rm = TRUE)) surface <- conductance_surface(covariates, coords, directions = 8) # site-level altitude, turned into a pairwise |difference| IBE predictor site_altitude <- data.frame(altitude = terra::extract(altitude, coords)[, 2]) g_joint <- mlpe_covariates(site_altitude) # joint isolation by environment and isolation by resistance fit_joint <- terradish(melip.Fst ~ altitude + forestcover, data = surface, conductance_model = loglinear_conductance, measurement_model = g_joint) # Expected change in genetic distance across the covariate's 10th-to-90th # percentile spread, on the response scale rather than the log scale. change <- mlpe_response_change(fit_joint) change # A narrower, interquartile contrast mlpe_response_change(fit_joint, probs = c(0.25, 0.75))
Tuning parameters shared by Newton-Raphson and BFGS quasi-Newton
optimizers inside terradish.
NewtonRaphsonControl( maxit = 100, ctol = sqrt(.Machine$double.eps), ftol = sqrt(.Machine$double.eps), etol = 10 * .Machine$double.eps, verbose = FALSE, eps = 1e-08, del = 1, ls.control = HagerZhangControl() )NewtonRaphsonControl( maxit = 100, ctol = sqrt(.Machine$double.eps), ftol = sqrt(.Machine$double.eps), etol = 10 * .Machine$double.eps, verbose = FALSE, eps = 1e-08, del = 1, ls.control = HagerZhangControl() )
maxit |
Maximum number of Newton or quasi-Newton steps. Increase if the optimizer reports a convergence warning on large or difficult problems. |
ctol |
Gradient convergence tolerance. Optimization stops when
the largest absolute projected gradient is below |
ftol |
Objective convergence tolerance. Optimization also stops when
the absolute change in the objective is below |
etol |
Eigenvalue threshold used to detect near-singular Hessians.
Absolute eigenvalues smaller than |
verbose |
Logical. If |
eps |
Box-constraint tolerance. A parameter is considered to be
"on the boundary" when it is within |
del |
Typical initial step length for quasi-Newton (BFGS) methods. Used to scale the initial approximate Hessian before any updates are available. |
ls.control |
Control object for the line search, created by
|
The projected gradient sets outward-pointing components at active bounds
to zero. A sufficiently small projected gradient gives convergence code 0.
A small objective change also gives code 0 if the projected gradient is
below sqrt(ctol). Otherwise the optimizer warns that it has stalled
and returns code 2. Reaching the iteration limit gives code 1.
For most resistance-surface models the defaults converge reliably. Consider changing them when:
The optimizer hits maxit before converging: increase
maxit.
You want a quick exploratory fit: reduce maxit (for example
5 to 10).
Fitting is slow because of oscillation: tighten ctol or
switch to a more conservative line search via ls.control.
A named list of class "NewtonRaphsonControl" holding the
settings passed to the optimizer. The elements are the arguments above,
validated and stored unchanged: maxit, ctol, ftol,
etol, verbose, eps, del, and
ls.control (itself a HagerZhangControl or
ArmijoControl list). Pass the object to the control
argument of terradish; inspect it with str(). The
list carries no fitted quantities, so nothing in it needs
back-transforming.
HagerZhangControl, ArmijoControl,
terradish
ctrl <- NewtonRaphsonControl(maxit = 25, verbose = FALSE) str(ctrl) # Relaxed tolerances for a quick exploratory run: ctrl_quick <- NewtonRaphsonControl(maxit = 10, ctol = 1e-3, ftol = 1e-3) data(melip) covariates <- c(terra::unwrap(melip.altitude), terra::unwrap(melip.forestcover)) names(covariates) <- c("altitude", "forestcover") covariates <- scale_covariates(terra::aggregate(covariates, fact = 3, na.rm = TRUE)) surface <- conductance_surface(covariates, terra::unwrap(melip.coords), directions = 8) # A tighter, longer-running control than the default fit <- terradish(melip.Fst ~ forestcover + altitude, data = surface, conductance_model = loglinear_conductance, measurement_model = mlpe, control = NewtonRaphsonControl(maxit = 200, ctol = 1e-8)) coef(fit) # The same fit, reporting each optimizer step. `verbose` is the simple # switch; it overrides whatever `control` says. fit_loud <- terradish(melip.Fst ~ forestcover + altitude, data = surface, conductance_model = loglinear_conductance, measurement_model = mlpe, control = ctrl_quick, verbose = TRUE)ctrl <- NewtonRaphsonControl(maxit = 25, verbose = FALSE) str(ctrl) # Relaxed tolerances for a quick exploratory run: ctrl_quick <- NewtonRaphsonControl(maxit = 10, ctol = 1e-3, ftol = 1e-3) data(melip) covariates <- c(terra::unwrap(melip.altitude), terra::unwrap(melip.forestcover)) names(covariates) <- c("altitude", "forestcover") covariates <- scale_covariates(terra::aggregate(covariates, fact = 3, na.rm = TRUE)) surface <- conductance_surface(covariates, terra::unwrap(melip.coords), directions = 8) # A tighter, longer-running control than the default fit <- terradish(melip.Fst ~ forestcover + altitude, data = surface, conductance_model = loglinear_conductance, measurement_model = mlpe, control = NewtonRaphsonControl(maxit = 200, ctol = 1e-8)) coef(fit) # The same fit, reporting each optimizer step. `verbose` is the simple # switch; it overrides whatever `control` says. fit_loud <- terradish(melip.Fst ~ forestcover + altitude, data = surface, conductance_model = loglinear_conductance, measurement_model = mlpe, control = ctrl_quick, verbose = TRUE)
Matched SNP and microsatellite genotypes for 20 retained diploid animals in each of 24 hypothetical forest ponds on a real southeastern Ohio landscape. The neutral SLiM simulation was motivated by a forest pond-breeding salamander. Pond locations, hydroperiods, demographic parameters, and genetic data are simulated. Elevation and land cover are derived from public USGS products.
data(ohio_ponds)data(ohio_ponds)
A list with 12 elements:
covariatesA packed terra::SpatRaster with 102 rows
and 102 columns at 100 m resolution in NAD83 / UTM zone 17N
(EPSG:26917). Layers are forest (fraction from 0 to 1),
slope (degrees), and elevation (meters).
forest500A packed raster on the same grid containing forest fraction smoothed with a Gaussian kernel of standard deviation 500 m. This is the context layer used to generate pond exchange.
pondsA 24-row data frame in population order. pond
is the identifier; x and y are UTM coordinates in meters;
cell is the 100 m raster cell; slope is in degrees;
edge_m is distance to the domain edge in meters;
forest_buffer_100m, forest_buffer_300m, and
forest_buffer_500m are cell-center forest fractions in circular
buffers of the indicated radius. hydroperiod is a hypothetical
pond covariate in days with no simulated genetic effect;
nlcd_30m is the native land-cover code at the pond;
displacement_from_trace25_m and candidate_count_at_selection
record the location-screening procedure; focal flags a sampled
pond; fold gives three spatial validation blocks.
samplesA 480-row data frame in genotype row order.
individual and pond are identifiers; population
is the one-based pond index; pedigree_id, parent_1, and
parent_2 are SLiM pedigree identifiers (-1 denotes an
unavailable parent); x and y are the pond coordinates.
raw_samplesThe same columns for 780 initial and additional captures before pedigree screening. Sampling began with 30 per pond, with additional captures where necessary to retain 20.
snpA 480 by 2500 integer matrix of alternate-allele dosage (0, 1, or 2), with individual row names and locus column names.
raw_snpA 780 by 2500 dosage matrix for the raw captures,
using exactly the same loci as snp.
snp_positionsThe 2500 SNP positions in base pairs along a 50 Mb simulated chromosome, in column order.
microsatA 480 by 48 integer matrix of repeat counts at 24
stepwise-mutation loci. Adjacent columns hold the two alleles at one
locus. Row names match snp; values are repeat counts, not dosage.
raw_microsatA 780 by 48 matrix for the raw captures at the same microsatellite loci.
microsat_lociA length-48 character vector assigning each microsatellite allele column to its locus.
simulationA named list recording simulation and mutation seeds, the 1200 compressed generations, demographic and marker parameters, the known 500 m forest context, the raw log-conductance coefficient of 6, the maximum pond-exchange probability of 0.025, the null hydroperiod effect, and the assumptions used in the teaching analyses.
Call terra::unwrap() on the two packed raster elements before use.
No downloads or simulation software are needed to analyze the bundled data.
All retained individuals have both marker panels from the same ancestry.
Screening removes known immediate pedigree relationships within ponds;
this is not a claim that the retained animals are unrelated at deeper
generations or that such relationships would be known in field samples.
Ponds were screened for native forest, at least 90 percent forest within 100 m, and at least 60 percent within 300 m. These are hypothetical design rules, not estimated habitat thresholds. Exchange has strong reproductive philopatry with a long tail among ponds. The exchange model uses endpoints on a resistance graph; it does not simulate observed animal travel paths.
The SNP panel was filtered for pooled retained-sample minor allele frequency of at least 0.05. Locus spacing does not establish independent loci. The effective degrees of freedom used in a Wishart example is an assumption, distinct from the SNP count. Spatial validation conditions on this globally ascertained panel, rather than validating marker discovery itself.
The source history was specified before fitting. Other histories and SNP panel sizes were checked during development; the bundled history was not selected for the strongest fit. The simulation illustrates an estimable mechanism, not calibration or validation of a species-specific demographic model. See the installed provenance file for sources and processing details.
vignette("data-generation", package = "terradish"),
vignette("getting-started", package = "terradish").
USGS 3D Elevation Program and USGS Annual National Land Cover
Database 2023 (Collection 1.0), processed to a common projected grid.
Source URLs, hashes, processing, and simulation provenance are recorded in
system.file("extdata", "ohio-ponds", "PROVENANCE.md",
package = "terradish").
cov_from_biallelic,
cov_from_genetic_data, conductance_surface,
vignette("getting-started", package = "terradish"),
vignette("gaussian-scale-optimization", package = "terradish").
data(ohio_ponds) covariates <- terra::unwrap(ohio_ponds$covariates) dim(ohio_ponds$snp) # individuals by SNPs dim(ohio_ponds$microsat) # individuals by two allele columns per locus table(ohio_ponds$samples$pond) terra::res(covariates) # map units are meters # Aggregate diploid dosage to counts of alternate alleles in each pond. groups <- factor(ohio_ponds$samples$pond, levels = ohio_ponds$ponds$pond) Y <- rowsum(ohio_ponds$snp, groups, reorder = FALSE) Y <- Y[ohio_ponds$ponds$pond, , drop = FALSE] N <- 2 * as.numeric(table(groups)) S <- cov_from_biallelic(Y, N) / N[1] round(S[1:4, 1:4], 3)data(ohio_ponds) covariates <- terra::unwrap(ohio_ponds$covariates) dim(ohio_ponds$snp) # individuals by SNPs dim(ohio_ponds$microsat) # individuals by two allele columns per locus table(ohio_ponds$samples$pond) terra::res(covariates) # map units are meters # Aggregate diploid dosage to counts of alternate alleles in each pond. groups <- factor(ohio_ponds$samples$pond, levels = ohio_ponds$ponds$pond) Y <- rowsum(ohio_ponds$snp, groups, reorder = FALSE) Y <- Y[ohio_ponds$ponds$pond, , drop = FALSE] N <- 2 * as.numeric(table(groups)) S <- cov_from_biallelic(Y, N) / N[1] round(S[1:4, 1:4], 3)
Combines differences calculated from site attributes with user-supplied pairwise matrices, such as geographic distance, in one measurement-model input. Values retain their supplied units and scaling.
pairwise_covariates(...)pairwise_covariates(...)
... |
Objects from |
No centering, scaling, or geometric correction is applied. For a Wishart model, each supplied distance must additionally produce a positive semidefinite centered kernel; the measurement-model constructor checks it. Site labels, when supplied on square matrices, must agree across inputs.
A classed numeric matrix with one row per unordered site pair and
one column per covariate. Rows follow the lower-triangle order used by R's
dist(). Both mlpe_covariates() and
wishart_covariates() accept this object.
Construct one pairwise_covariates object to use the same
environmental transforms and geographic distances in both likelihood
families. Their fitted conductance surfaces are conditional on these terms,
and conductance signs can change when the mean structure changes. Report
fits with and without pairwise effects. A uniform ~ 1 conductance
model with pairwise terms represents IBD plus IBE, not IBE alone.
A positive environmental coefficient means differentiation increases with
environmental difference conditional on resistance. It does not distinguish
assortative mating, dispersal filtering, local adaptation, or site variance
that rises at environmental extremes. MLPE coefficients are unconstrained;
Wishart kernel weights are nonnegative. Compare their effects in
resistance-distance units with terradish_ibe_ratio, which
divides by the resistance coefficient and propagates joint uncertainty.
Ratios are undefined when that denominator is zero.
pairwise_endpoint_covariates,
mlpe_covariates, wishart_covariates
climate <- pairwise_endpoint_covariates(data.frame(temp = c(4, 7, 9))) geography <- dist(cbind(c(0, 1, 3), c(0, 2, 1))) z <- pairwise_covariates(climate, geographic = geography) colnames(z)climate <- pairwise_endpoint_covariates(data.frame(temp = c(4, 7, 9))) geography <- dist(cbind(c(0, 1, 3), c(0, 2, 1))) z <- pairwise_covariates(climate, geographic = geography) colnames(z)
Constructs pairwise environmental-mismatch covariates from site-level
values. The result can be used alongside isolation-by-resistance (IBR)
terms to model isolation by environment (IBE) within
mlpe_covariates.
pairwise_endpoint_covariates( x, coords = NULL, transform = c("absdiff", "sqdiff", "euclidean", "manhattan"), scale = FALSE )pairwise_endpoint_covariates( x, coords = NULL, transform = c("absdiff", "sqdiff", "euclidean", "manhattan"), scale = FALSE )
x |
Site-level covariates. Supported inputs:
|
coords |
Required when |
transform |
How to collapse site-level values into a pairwise environmental-mismatch measure.
|
scale |
Logical. If |
The function produces a matrix of pairwise
endpoint-difference covariates, where is the number of focal sites
and is the number of output columns (one per input covariate for
"absdiff" and "sqdiff"; one for "euclidean" and
"manhattan"). Rows correspond to the lower triangle of the
pairwise distance matrix, scanned column by column.
The returned object has class "terradish_pairwise_covariates". This
class carries the original site-level covariate matrix and the chosen
transform as attributes, so that terradish_cv_folds can
automatically rebuild the correct pairwise covariates for each train/test
split.
If x already has class "terradish_pairwise_covariates", it is
returned unchanged (pass-through).
A numeric matrix of class "terradish_pairwise_covariates"
with one row per unordered site pair and named columns reflecting the
chosen transform (e.g. "absdiff_altitude",
"euclidean").
Construct one pairwise_covariates object to use the same
environmental transforms and geographic distances in both likelihood
families. Their fitted conductance surfaces are conditional on these terms,
and conductance signs can change when the mean structure changes. Report
fits with and without pairwise effects. A uniform ~ 1 conductance
model with pairwise terms represents IBD plus IBE, not IBE alone.
A positive environmental coefficient means differentiation increases with
environmental difference conditional on resistance. It does not distinguish
assortative mating, dispersal filtering, local adaptation, or site variance
that rises at environmental extremes. MLPE coefficients are unconstrained;
Wishart kernel weights are nonnegative. Compare their effects in
resistance-distance units with terradish_ibe_ratio, which
divides by the resistance coefficient and propagates joint uncertainty.
Ratios are undefined when that denominator is zero.
mlpe_covariates, terradish_cv_folds,
terradish
library(terra) data(melip) melip.altitude <- terra::unwrap(melip.altitude) melip.coords <- terra::unwrap(melip.coords) z_abs <- pairwise_endpoint_covariates(melip.altitude, melip.coords, transform = "absdiff", scale = TRUE) head(z_abs) site_env <- data.frame( altitude = scale(as.numeric(terra::extract(melip.altitude, melip.coords)[, 2])), xcoord = scale(terra::geom(melip.coords)[, "x"]) ) z_env <- pairwise_endpoint_covariates(site_env, transform = "euclidean") head(z_env)library(terra) data(melip) melip.altitude <- terra::unwrap(melip.altitude) melip.coords <- terra::unwrap(melip.coords) z_abs <- pairwise_endpoint_covariates(melip.altitude, melip.coords, transform = "absdiff", scale = TRUE) head(z_abs) site_env <- data.frame( altitude = scale(as.numeric(terra::extract(melip.altitude, melip.coords)[, 2])), xcoord = scale(terra::geom(melip.coords)[, "x"]) ) z_env <- pairwise_endpoint_covariates(site_env, transform = "euclidean") head(z_env)
Runs a PCA on allele-frequency data from an adegenet::genind object
and returns Euclidean distances computed on the retained principal component
scores.
pca_dist(gi, n_axes = 64, scale = TRUE)pca_dist(gi, n_axes = 64, scale = TRUE)
gi |
A |
n_axes |
Number of principal component axes to retain. Capped at the total number of available axes. |
scale |
Logical. If |
Missing genotypes in gi are imputed by the allele-mean method
(equivalent to adegenet::tab(gi, NA.method = "mean")), then a
standard PCA via prcomp is applied to the allele
frequency table. Euclidean distances are then computed on the first
n_axes scores.
This is a convenience wrapper designed for users who prefer to work from
a genind object. For biallelic SNP data the more direct
dist_from_biallelic is generally faster and does not require
the adegenet package.
A symmetric numeric distance matrix with one row/column per
individual in gi.
dist_from_biallelic, cov_from_genetic_data
if (requireNamespace("adegenet", quietly = TRUE)) { data(nancycats, package = "adegenet") d <- pca_dist(nancycats, n_axes = 4) dim(d) }if (requireNamespace("adegenet", quietly = TRUE)) { data(nancycats, package = "adegenet") d <- pca_dist(nancycats, n_axes = 4) dim(d) }
Five plot types for objects returned by terradish:
"fit"Observed vs. fitted response values. Distance-response
models are shown as pairwise genetic distance against fitted resistance
distance; covariance-response models such as
wishart_covariance are shown as observed genetic covariance
against fitted genetic covariance.
"surface"Fitted conductance surface with asymptotic confidence interval bounds, displayed in three side-by-side panels.
"marginal"Marginal association of each raster covariate with
conductance, varying one covariate at a time while averaging over the
observed values of all other model covariates, with a 95% pointwise
confidence band via the delta method. For Gaussian scale-aware
conductance models, these curves are conditional on the fitted
sigma values and are shown on the smoothed covariate scale. For
spline-expanded conductance models from
smooth_loglinear_conductance, panels are shown against the
original covariate named inside each s() term rather than against
the individual basis columns.
"marginal_response"Approximate response-scale marginal
associations obtained by mapping the marginal conductance curve through the
fitted measurement-model mean and adding predictive bands that combine
theta uncertainty, conditional phi uncertainty, and the
residual variance implied by tau for distance-response models.
Covariance-response fits from wishart_covariance are shown
as the average fitted genetic covariance over the modeled lower triangle.
For Gaussian scale-aware conductance models, the predictive bands are
likewise conditional on the fitted sigma values. Each curve sets
one covariate to a common value in every cell. In a model with a
single covariate this makes conductance uniform, so the curve is the
"marginal" conductance curve inverted and rescaled; the plot is
most informative when other covariates still vary across the
landscape.
"sigma"For Gaussian scale-aware conductance models, plots
the fitted Gaussian kernel against distance for each sigma term,
with a dashed line at the fitted distance containing 90% of
the one-dimensional kernel mass and a shaded interval from the Wald
confidence interval of that kernel-mass distance. These distances
summarize the fitted smoothing kernel and are not movement or dispersal
distances.
## S3 method for class 'terradish' plot( x, type = c("marginal_response", "fit", "surface", "marginal", "sigma"), data = NULL, covariates = NULL, marginal_covariates = NULL, conductance_model = NULL, distance_per_map_unit = NULL, distance_unit = NULL, quantile = 0.95, n = NULL, support = c("none", "focal"), support_probs = c(0, 1), clamp_covariates = NULL, ... )## S3 method for class 'terradish' plot( x, type = c("marginal_response", "fit", "surface", "marginal", "sigma"), data = NULL, covariates = NULL, marginal_covariates = NULL, conductance_model = NULL, distance_per_map_unit = NULL, distance_unit = NULL, quantile = 0.95, n = NULL, support = c("none", "focal"), support_probs = c(0, 1), clamp_covariates = NULL, ... )
x |
A fitted |
type |
One of |
data |
The |
covariates |
Optional |
marginal_covariates |
Optional character vector used with
|
conductance_model |
Optional conductance model factory used when fitting
|
distance_per_map_unit |
Optional positive scalar used by
|
distance_unit |
Optional label used with
|
quantile |
Confidence level for interval bands. Default |
n |
Number of evaluation points for each marginal-association curve.
Defaults to |
support |
Optional support constraint used for
|
support_probs |
Optional length-2 probability vector used with
|
clamp_covariates |
Optional character vector of covariate names to clamp
when |
... |
Additional graphical parameters forwarded to
|
For type = "surface", "marginal", and
"marginal_response", optional support-constrained evaluation can be
requested with support = "focal" to clamp selected covariates to
empirical focal-site support before prediction.
A plot object that is returned visibly so base R auto-printing works as expected:
"fit": a ggplot object with observed-vs-fitted
pairwise distances or genetic covariance, according to the response model.
"surface", "marginal", "marginal_response",
and "sigma": either a single ggplot object (one panel) or
a named list of ggplot objects (multiple panels), with one plot
per panel/covariate.
vignette("getting-started", package = "terradish"),
vignette("gaussian-scale-optimization", package = "terradish"),
vignette("spline-conductance", package = "terradish").
terradish, conductance,
fitted.terradish, loglinear_conductance,
smooth_loglinear_conductance
data(melip) melip.altitude <- terra::unwrap(melip.altitude) melip.forestcover <- terra::unwrap(melip.forestcover) melip.coords <- terra::unwrap(melip.coords) # Build surface with scaled covariates. The raster is coarsened so the # example runs quickly; use the full resolution in a real analysis. covariates_scaled <- c(melip.altitude, melip.forestcover) names(covariates_scaled) <- c("altitude", "forestcover") covariates_scaled <- terra::aggregate(covariates_scaled, fact = 3, na.rm = TRUE) covariates_scaled <- scale_covariates(covariates_scaled) surface <- conductance_surface(covariates_scaled, melip.coords, directions = 8, saveStack = TRUE) fit <- terradish(melip.Fst ~ altitude + forestcover, surface, loglinear_conductance, mlpe) # Observed vs. fitted pairwise genetic distances plot(fit, type = "fit") # Fitted conductance surface with 95% CI (three-panel figure) plot(fit, type = "surface", data = surface) # Marginal-association plots on the original covariate scale plot(fit, type = "marginal", data = surface) # Approximate response-scale marginal associations with predictive bands plot(fit, type = "marginal_response", data = surface) # For Gaussian scale-aware fits, visualize the fitted sigma kernels # plot(fit_gaussian, type = "sigma")data(melip) melip.altitude <- terra::unwrap(melip.altitude) melip.forestcover <- terra::unwrap(melip.forestcover) melip.coords <- terra::unwrap(melip.coords) # Build surface with scaled covariates. The raster is coarsened so the # example runs quickly; use the full resolution in a real analysis. covariates_scaled <- c(melip.altitude, melip.forestcover) names(covariates_scaled) <- c("altitude", "forestcover") covariates_scaled <- terra::aggregate(covariates_scaled, fact = 3, na.rm = TRUE) covariates_scaled <- scale_covariates(covariates_scaled) surface <- conductance_surface(covariates_scaled, melip.coords, directions = 8, saveStack = TRUE) fit <- terradish(melip.Fst ~ altitude + forestcover, surface, loglinear_conductance, mlpe) # Observed vs. fitted pairwise genetic distances plot(fit, type = "fit") # Fitted conductance surface with 95% CI (three-panel figure) plot(fit, type = "surface", data = surface) # Marginal-association plots on the original covariate scale plot(fit, type = "marginal", data = surface) # Approximate response-scale marginal associations with predictive bands plot(fit, type = "marginal_response", data = surface) # For Gaussian scale-aware fits, visualize the fitted sigma kernels # plot(fit_gaussian, type = "sigma")
Compact display for the object returned by
covariance_response_power: the settings the sweep ran under,
followed by the scenario-level summary table.
## S3 method for class 'terradish_covariance_power' print(x, ...)## S3 method for class 'terradish_covariance_power' print(x, ...)
x |
An object of class |
... |
Ignored, present for compatibility with the |
How to read the output. The header records what the sweep did:
how many simulated data sets per design (nsim), which sample sizes,
and which site-selection strategies. Judge the numbers below it against
that nsim: with a small nsim the Monte Carlo error on a rate
is substantial, so treat a demonstration run as indicative only.
The table beneath is x$summary, one row per scenario (strategy by
sample size by fitted model). fit_rate is the proportion of
simulations that produced a usable fit; a value well below 1 can reflect
weak information, numerical difficulty, boundary fits, or a candidate model
that is poorly matched to the scenario. conductance_power is the
proportion exceeding the specified conductance-correlation threshold,
mean_conductance_cor summarizes correlation with the generating
conductance surface, and the model-selection columns report how often each
candidate had the lowest AIC or AICc among usable fits.
Per-coefficient results (power, bias, RMSE, and interval coverage) are not
printed, because there is one row per parameter per scenario. Read them
from x$parameter_summary. Power there is the proportion of
simulations in which the coefficient was both correctly signed and
significant, so it is the quantity to compare against a target such as 0.8.
x, invisibly. Called for the printed output.
covariance_response_power for the sweep itself and
what each returned component contains,
simulate_covariance_response for a single simulated data
set, and terradish for fitting one. See
vignette("simulation-design", package = "terradish") for a worked
study-design walkthrough.
data(melip) covariates <- c(terra::unwrap(melip.altitude), terra::unwrap(melip.forestcover)) names(covariates) <- c("altitude", "forestcover") # coarsened, and only a handful of simulations, so the example is quick covariates <- scale_covariates(terra::aggregate(covariates, fact = 3, na.rm = TRUE)) surface <- conductance_surface(covariates, terra::unwrap(melip.coords), directions = 8, saveStack = TRUE) power <- covariance_response_power( theta = c(forestcover = 0.5, altitude = -0.4), formula = ~ forestcover + altitude, data = surface, sample_sizes = c(8, 12), strategies = "spacefill", tau = 1, sigma = 0.2, nu = 100, nsim = 3, # raise to 200+ for a real power estimate seed = 1, control = NewtonRaphsonControl(maxit = 8) ) power # settings header plus the scenario summary power$parameter_summary # per-coefficient power, bias, RMSE, coveragedata(melip) covariates <- c(terra::unwrap(melip.altitude), terra::unwrap(melip.forestcover)) names(covariates) <- c("altitude", "forestcover") # coarsened, and only a handful of simulations, so the example is quick covariates <- scale_covariates(terra::aggregate(covariates, fact = 3, na.rm = TRUE)) surface <- conductance_surface(covariates, terra::unwrap(melip.coords), directions = 8, saveStack = TRUE) power <- covariance_response_power( theta = c(forestcover = 0.5, altitude = -0.4), formula = ~ forestcover + altitude, data = surface, sample_sizes = c(8, 12), strategies = "spacefill", tau = 1, sigma = 0.2, nu = 100, nsim = 3, # raise to 200+ for a real power estimate seed = 1, control = NewtonRaphsonControl(maxit = 8) ) power # settings header plus the scenario summary power$parameter_summary # per-coefficient power, bias, RMSE, coverage
Compact display for the object returned by
terradish_assess_settings: the graph profile, the recommended
computational settings and why each was chosen, the probe timings behind
them, and any warnings the assessment raised.
## S3 method for class 'terradish_setting_assessment' print(x, digits = max(3L, getOption("digits") - 3L), ...)## S3 method for class 'terradish_setting_assessment' print(x, digits = max(3L, getOption("digits") - 3L), ...)
x |
An object of class |
digits |
Number of significant digits used when formatting the probe timing tables. |
... |
Ignored, present for compatibility with the |
How to read the output. It comes in four parts.
Graph reports the size of the problem: vertices (active raster cells), edges, focal sites, and conductance parameters. This is the number that decides whether any of the rest matters. Below roughly 50,000 vertices the defaults are almost always fine; above a few hundred thousand, solver choice dominates the runtime.
Recommended settings lists the optimizer, line search, solver, and
approximation the probes favored, each followed by a parenthesized reason.
Pass them straight through with fit <- terradish(..., optimizer =
rec$optimizer, control = rec$control, solver = rec$solver), where
rec <- assessment$recommended. These choices change only the
computational path, never the estimates.
Defaults comparison is the one-line verdict: whether the assessment agreed with the package defaults. If it says it matched, there is nothing to do and you can drop the assessment from your script.
Probes are the measurements behind the recommendation. The optimizer table gives elapsed time and the log-likelihood each candidate reached in the probe budget, so a candidate that is fast but reaches a worse log-likelihood is not actually better. The solver table shows the three fastest direct factorizations split into setup and solve time; setup dominates when a fit re-factorizes often, solve time when the focal set is large. Notes collects anything that needs attention, such as a solver that failed to converge during probing.
x, invisibly. Called for the printed output. The machine
readable pieces are x$profile, x$recommended,
x$reasons, x$benchmarks, x$comparison, and
x$notes.
terradish_assess_settings for the assessment itself
and what each component holds, terradish_solver_benchmark
for benchmarking solvers alone, and terradish for the fit
the recommendations feed. See
vignette("large-landscapes", package = "terradish") for when this
is worth running.
data(melip) covariates <- c(terra::unwrap(melip.altitude), terra::unwrap(melip.forestcover)) names(covariates) <- c("altitude", "forestcover") # coarsened for the example; assessment earns its keep on large graphs covariates <- scale_covariates(terra::aggregate(covariates, fact = 3, na.rm = TRUE)) surface <- conductance_surface(covariates, terra::unwrap(melip.coords), directions = 8, saveStack = TRUE) assessment <- terradish_assess_settings( melip.Fst ~ forestcover + altitude, data = surface, measurement_model = mlpe, probe_maxit = 2) assessment # the four-part report described above assessment$comparison$summary # one-line verdict against the defaults assessment$recommended$solver # the settings, ready to reusedata(melip) covariates <- c(terra::unwrap(melip.altitude), terra::unwrap(melip.forestcover)) names(covariates) <- c("altitude", "forestcover") # coarsened for the example; assessment earns its keep on large graphs covariates <- scale_covariates(terra::aggregate(covariates, fact = 3, na.rm = TRUE)) surface <- conductance_surface(covariates, terra::unwrap(melip.coords), directions = 8, saveStack = TRUE) assessment <- terradish_assess_settings( melip.Fst ~ forestcover + altitude, data = surface, measurement_model = mlpe, probe_maxit = 2) assessment # the four-part report described above assessment$comparison$summary # one-line verdict against the defaults assessment$recommended$solver # the settings, ready to reuse
Transforms each layer of a SpatRaster and stores the original-scale
offset and divisor as a "terradish_scale" attribute. When the scaled
raster is used to build a conductance_surface, marginal-association
plots (plot(fit, type = "marginal", data = surface)) can
automatically back-transform x-axes to the original covariate units.
scale_covariates( covariates, method = c("zscore", "minmax"), center = TRUE, scale = TRUE, reference = NULL )scale_covariates( covariates, method = c("zscore", "minmax"), center = TRUE, scale = TRUE, reference = NULL )
covariates |
A |
method |
Scaling method applied to each layer independently.
|
center |
Logical. Only used when |
scale |
Logical. Only used when |
reference |
A previous scaled raster or its |
Scaling is strongly recommended before fitting conductance models. In the
loglinear model, conductance is . When x
has a large range (e.g. altitude in meters), a small change in
produces enormous conductance differences, making the objective surface
poorly conditioned and the optimizer slow to converge.
Missing values (NA) are excluded from the computation of the mean,
standard deviation, minimum, and range, and are left as NA in the
output.
If a layer is constant (all values identical), the scale is set to 1 to
avoid division by zero; the layer is then returned unchanged (or centered
to 0 if center = TRUE).
The "terradish_scale" attribute is a named list with one element per
layer; each element is a named numeric vector with entries center and
scale (the subtracted offset and the divisor, respectively).
crop_to_focal_buffer and conductance_surface preserve this
attribute.
A SpatRaster with transformed values and a
"terradish_scale" attribute.
scale_to_0_1, conductance_surface
library(terra) r <- c(terra::rast(nrows = 2, ncols = 2, vals = c(100, 200, 300, NA)), terra::rast(nrows = 2, ncols = 2, vals = c(0.2, 0.5, 0.4, NA))) names(r) <- c("altitude", "forestcover") scaled_r <- scale_covariates(r) attr(scaled_r, "terradish_scale") # per-layer center and scale # min-max scaling to [0, 1] scaled_mm <- scale_covariates(r, method = "minmax") # Preserve the training transformation on new values, matched by layer name. new_scaled <- scale_covariates(r + 10, reference = scaled_r) terra::values(new_scaled) # Values can fall outside the training range; they are not re-centered.library(terra) r <- c(terra::rast(nrows = 2, ncols = 2, vals = c(100, 200, 300, NA)), terra::rast(nrows = 2, ncols = 2, vals = c(0.2, 0.5, 0.4, NA))) names(r) <- c("altitude", "forestcover") scaled_r <- scale_covariates(r) attr(scaled_r, "terradish_scale") # per-layer center and scale # min-max scaling to [0, 1] scaled_mm <- scale_covariates(r, method = "minmax") # Preserve the training transformation on new values, matched by layer name. new_scaled <- scale_covariates(r + 10, reference = scaled_r) terra::values(new_scaled) # Values can fall outside the training range; they are not re-centered.
Rescales numeric values so the minimum non-missing value maps to 0 and
the maximum maps to 1. terra::SpatRaster inputs are rescaled
layer-by-layer using only the finite (non-NA) values.
scale_to_0_1(x)scale_to_0_1(x)
x |
Numeric vector, matrix, |
The transformation applied to each element is
, computed over non-NA values.
NA values are propagated unchanged.
If all non-missing values are identical (constant layer or vector), the
output is set to 0 for all non-missing entries rather than producing
NaN from a zero-range denominator.
For rasters, each layer is rescaled independently using its own minimum and maximum.
An object of the same class as x with values in
[0, 1] (non-NA entries only).
scale_covariates for standardized (z-score or min-max)
scaling with metadata retained for back-transformation.
scale_to_0_1(c(2, 4, 6)) scale_to_0_1(c(5, 5, NA)) # constant non-NA values become 0 library(terra) r <- terra::rast(nrows = 2, ncols = 2, vals = c(1, 3, 5, NA)) scale_to_0_1(r)scale_to_0_1(c(2, 4, 6)) scale_to_0_1(c(5, 5, NA)) # constant non-NA values become 0 library(terra) r <- terra::rast(nrows = 2, ncols = 2, vals = c(1, 3, 5, NA)) scale_to_0_1(r)
Simulates one or more covariance response matrices from a known
conductance-resistance relationship, using the same Wishart covariance model
fitted by wishart_covariance.
simulate_covariance_response( theta, formula, data, conductance_model = loglinear_conductance, tau = 1, sigma = 0, nu, nsim = 1, seed = NULL, cores = 1L )simulate_covariance_response( theta, formula, data, conductance_model = loglinear_conductance, tau = 1, sigma = 0, nu, nsim = 1, seed = NULL, cores = 1L )
theta |
Conductance parameters. May be supplied as a numeric vector or
a one-row matrix. If unnamed, values are matched in the order implied by
|
formula |
Model formula specifying the conductance covariates. The left hand side, if supplied, is ignored. |
data |
A |
conductance_model |
Conductance-model factory, such as
|
tau |
Nonnegative scaling applied to the conductance-implied covariance matrix. |
sigma |
Nonnegative nugget variance added to the diagonal. |
nu |
Effective Wishart degrees of freedom: controls the amount of
sampling noise in the simulated covariance: larger |
nsim |
Number of covariance matrices to simulate. |
seed |
Optional random seed. |
cores |
Number of cores passed to |
The helper first computes the conductance-implied covariance matrix
E(theta) from the supplied resistance surface. It then forms
Sigma = tau * E(theta) + sigma * I and simulates
S ~ Wishart(nu, Sigma) / nu. This makes the returned covariance
matrices directly compatible with wishart_covariance.
These results are internal to the assumed model. Simulating and fitting
at the same incorrect nu cannot reveal that error. Use
covariance_response_power with different nu and
nu_fit to examine its effect on coverage.
A list containing:
covariance |
A covariance matrix if |
Sigma |
The covariance matrix used as the Wishart scale matrix after
applying |
E |
The conductance-implied covariance matrix returned by
|
theta |
The validated conductance parameter matrix used for simulation. |
tau |
The supplied |
sigma |
The supplied |
nu |
The supplied |
r1 <- terra::rast(nrows = 3, ncols = 3, vals = 1:9) r2 <- terra::rast(nrows = 3, ncols = 3, vals = c(9, 1, 4, 3, 8, 2, 5, 7, 6)) covariates <- c(r1, r2) names(covariates) <- c("x1", "x2") pts <- terra::vect(matrix(c(0.5, 0.5, 1.5, 1.5, 2.5, 2.5), ncol = 2, byrow = TRUE), type = "points") surface <- conductance_surface(covariates, pts, directions = 4) sim <- simulate_covariance_response( theta = c(x1 = 0.3, x2 = -0.2), formula = ~ x1 + x2, data = surface, tau = 0.8, sigma = 0.5, nu = 20, seed = 1 ) sim$covariancer1 <- terra::rast(nrows = 3, ncols = 3, vals = 1:9) r2 <- terra::rast(nrows = 3, ncols = 3, vals = c(9, 1, 4, 3, 8, 2, 5, 7, 6)) covariates <- c(r1, r2) names(covariates) <- c("x1", "x2") pts <- terra::vect(matrix(c(0.5, 0.5, 1.5, 1.5, 2.5, 2.5), ncol = 2, byrow = TRUE), type = "points") surface <- conductance_surface(covariates, pts, directions = 4) sim <- simulate_covariance_response( theta = c(x1 = 0.3, x2 = -0.2), formula = ~ x1 + x2, data = surface, tau = 0.8, sigma = 0.5, nu = 20, seed = 1 ) sim$covariance
Removes components that are useful for prediction or diagnostics but can
make a fitted terradish object expensive to serialize. The fitted
coefficients, likelihood, Hessian, fitted response, convergence diagnostics,
and approximation metadata are retained unless explicitly dropped.
slim_terradish(object, drop = c("submodels", "leverage"))slim_terradish(object, drop = c("submodels", "leverage"))
object |
A fitted |
drop |
Character vector naming optional components to remove. Supported
values are |
A slim object remains suitable for print(), summary(),
coef(), vcov(), confint(), logLik(), AIC(), and fitted-value
extraction and residual-permutation simulation. Operations that must
reevaluate the conductance or measurement model, including conductance-
surface prediction and most plot types, require the retained model closures
and will stop with an informative error if those closures were dropped.
Slimming is irreversible. Retain the original fit, model formula, graph, and response when later prediction, simulation, or refitting may be needed.
A fitted terradish object with a storage element that
records the removed components and the object's size before and after
slimming.
data(melip) altitude <- terra::unwrap(melip.altitude) forestcover <- terra::unwrap(melip.forestcover) coords <- terra::unwrap(melip.coords) covariates <- c(altitude, forestcover) names(covariates) <- c("altitude", "forestcover") surface <- conductance_surface(covariates, coords) fit <- terradish(melip.Fst ~ altitude, surface, measurement_model = leastsquares, control = NewtonRaphsonControl(maxit = 2)) fit_slim <- slim_terradish(fit) object.size(fit_slim) <= object.size(fit) coef(fit_slim)data(melip) altitude <- terra::unwrap(melip.altitude) forestcover <- terra::unwrap(melip.forestcover) coords <- terra::unwrap(melip.coords) covariates <- c(altitude, forestcover) names(covariates) <- c("altitude", "forestcover") surface <- conductance_surface(covariates, coords) fit <- terradish(melip.Fst ~ altitude, surface, measurement_model = leastsquares, control = NewtonRaphsonControl(maxit = 2)) fit_slim <- slim_terradish(fit) object.size(fit_slim) <= object.size(fit) coef(fit_slim)
Returns a function of class "terradish_conductance_model" that
represents a log-linear conductance surface after expanding selected
numeric covariates into spline basis columns. The result is an unpenalized
regression spline with a fixed number of coefficients, fitted through the
selected terradish measurement likelihood.
smooth_loglinear_conductance( formula, x, df = 4L, basis = c("ns", "bs"), degree = 3L, intercept = FALSE )smooth_loglinear_conductance( formula, x, df = 4L, basis = c("ns", "bs"), degree = 3L, intercept = FALSE )
formula |
Model formula describing which spatial covariates drive
conductance. Smooth terms are written as |
x |
Data frame of spatial covariates extracted from a
|
df |
Default degrees of freedom for smooth terms that do not supply
|
basis |
Default spline basis. |
degree |
B-spline polynomial degree used when |
intercept |
Logical; include intercept columns within each spline
basis? The default is |
The model first expands all s() terms into basis columns and then
applies the same positive log-link used by loglinear_conductance:
Each spline column is centered over the active graph cells. The fitted knots, boundary knots, degree, and column centers are retained and reused for plotting, focal-support prediction, and coarse warm starts. Centering changes the arbitrary conductance reference level but preserves the likelihood and fitted conductance coefficients. Uncertainty bands are relative to the landscape-mean spline contribution to log conductance.
where is the row of the expanded spline/parametric design matrix
for grid cell . The same conductance factory works with MLPE and
Wishart measurement models.
Smoothing parameters are not estimated in this first implementation. The
effective smoothness is controlled by fixed basis dimension through
df or k; use small values for stable first-pass model
comparison.
AIC and AICc at nominal Wishart information can over-select flexible
curves. Prefer spatial CV for selecting conductance terms, and examine
sensitivity to nu before interpreting uncertainty. Tails beyond
the sampled covariate range are poorly identified. A selected curve can
describe how a process maps onto the graph without identifying an
ecological response mechanism.
summary(fit)$spline_monotonicity describes each fitted smooth over
the range at focal sites. It reports whether the curve is monotone, its
direction, and the number of derivative sign changes. This is a shape
diagnostic, not an uncertainty test.
A function of class "terradish_conductance_model" that
accepts a numeric vector of conductance parameters theta and
returns conductance values plus derivative functions used internally by
the optimizer. Derivatives are with respect to the expanded basis columns.
See vignette("spline-conductance", package = "terradish") for
fitted conductance curves, genetic-response plots, and model comparison.
loglinear_conductance, mlpe,
conductance_surface, terradish
x <- data.frame(altitude = seq(-1, 1, length.out = 8), forestcover = c(0.2, 0.4, 0.6, 0.7, 0.5, 0.3, 0.2, 0.1)) model <- smooth_loglinear_conductance(~ forestcover + s(altitude, df = 3), x) theta <- attr(model, "default") fit <- model(theta) fit$conductancex <- data.frame(altitude = seq(-1, 1, length.out = 8), forestcover = c(0.2, 0.4, 0.6, 0.7, 0.5, 0.3, 0.2, 0.1)) model <- smooth_loglinear_conductance(~ forestcover + s(altitude, df = 3), x) theta <- attr(model, "default") fit <- model(theta) fit$conductance
Uses maximum likelihood to fit a parameterized conductance surface to genetic data, given a function relating spatial data to conductance (a "conductance model") and a function linking the graph kernel to an observed genetic distance or covariance matrix (a "measurement model").
terradish( formula, data, conductance_model = loglinear_conductance, measurement_model = mlpe, nu = NULL, theta = NULL, leverage = FALSE, nonnegative = TRUE, conductance = TRUE, optimizer = c("newton", "bfgs", "auto"), verbose = FALSE, control = NewtonRaphsonControl(ctol = 1e-06, ftol = 1e-06), validate = FALSE, cores = 1L, curvature = c("exact", "gauss_newton"), solver = c("direct", "auto", "amg"), solver_control = NULL, approximation = c("none", "landmark", "coarse_raster"), approximation_control = NULL, measurement_control = NULL, slim = FALSE )terradish( formula, data, conductance_model = loglinear_conductance, measurement_model = mlpe, nu = NULL, theta = NULL, leverage = FALSE, nonnegative = TRUE, conductance = TRUE, optimizer = c("newton", "bfgs", "auto"), verbose = FALSE, control = NewtonRaphsonControl(ctol = 1e-06, ftol = 1e-06), validate = FALSE, cores = 1L, curvature = c("exact", "gauss_newton"), solver = c("direct", "auto", "amg"), solver_control = NULL, approximation = c("none", "landmark", "coarse_raster"), approximation_control = NULL, measurement_control = NULL, slim = FALSE )
formula |
A formula with an observed genetic distance or covariance
matrix on the left-hand side and conductance covariates from |
data |
An object of class |
conductance_model |
A function of class
|
measurement_model |
A function of class
|
nu |
Effective Wishart degrees of freedom passed to measurement models
that require it, such as |
theta |
Starting values for optimization |
leverage |
Compute influence measures and leverage? |
nonnegative |
Force regression-like |
conductance |
Retained for backward compatibility. Only
|
optimizer |
The optimization algorithm to use: |
verbose |
Logical. Should the optimizer report its progress? The
default |
control |
A list containing options for the optimization routine (see |
validate |
Numerical validation of leverage via package |
cores |
Number of worker processes. |
curvature |
Curvature used for optimization steps and for the returned
covariance matrix. |
solver |
Linear-system solver. |
solver_control |
Optional named list of solver settings passed to
|
approximation |
Exploratory approximation used during optimization.
|
approximation_control |
Optional named list controlling the landmark or
coarse-raster approximation. Landmark entries include
|
measurement_control |
Optional |
slim |
Logical. If |
A parameterized conductance surface maps raster covariates to positive vertex
conductance. Vertex values define weighted edges and a graph Laplacian
. Its generalized inverse is the
shared graph-process kernel. Distance likelihoods use resistance distances
derived from , whereas covariance likelihoods use the kernel
directly. The selected probability model is called the measurement model.
These alternatives describe how related genetic summaries are observed; they
are not competing biological process theories.
For example, a log-linear choice of conductance model is:
vertex_conductance[i] = exp(covariates[i,] %*% theta)
where theta are unknown parameters, covariates is a design
matrix with each row representing a vertex in the graph (cell in the
raster). The conductance of a given edge (an off-diagonal entry in the
graph Laplacian) is:
edge_conductance[i,j] = vertex_conductance[i] + vertex_conductance[j]
if argument conductance = TRUE, and
edge_conductance[i,j] = 1/(1/vertex_conductance[i] + 1/vertex_conductance[j])
otherwise. This alternative parameterization is not currently implemented,
so conductance must be TRUE. This differs by a factor of two
from CIRCUITSCAPE, where the edge
conductance/resistance is the average of the vertex conductance/resistance.
terradish estimates theta by maximizing the selected
measurement likelihood for the observed response. The optimization is
done via Newton's method (default; requires computation of Hessian during
optimization), via the BFGS algorithm (requires gradient only during
optimization) if optimizer = "bfgs", or via a simple parameter-count
heuristic if optimizer = "auto". In all cases, the final returned fit
is evaluated at the optimized parameters using the requested
curvature for inference.
Conductance coefficients are conditional model associations. Their sign and magnitude depend on covariate coding, other terms, graph construction, response, measurement likelihood, and candidate set. They are not direct estimates of causal effects, habitat suitability, migration, dispersal, or organismal movement rates.
For an explanation of how categorical spatial covariates are handled, see
details of conductance_surface and the examples below.
The dummy coding of categorical covariates is done by the function passed to
conductance_model (e.g. terradish::loglinear_conductance).
Large rasters. For large continuous rasters, two opt-in helpers can
reduce runtime. First, build the graph with
conductance_surface(..., crop_buffer = ) when focal sites occupy only
part of the raster. This removes vertices outside the buffered sampling
extent before fitting. Cropping changes resistance: one sensitivity audit
found increases of 15–23% with a two-cell buffer and 1–2% with a
ten-cell buffer. These values are case-specific. Refit with a larger buffer
to assess sensitivity on your landscape. Second, use
approximation = "coarse_raster" with
approximation_control = list(factor = c(4, 2), exact_refine = TRUE)
to optimize on one or more aggregated rasters before refining on the original
graph. With exact_refine = TRUE, the returned coefficients and
likelihood are from the full-resolution graph; the coarse fits are only
starting values. To keep the full-resolution cleanup deliberately short, pass
refine_control = NewtonRaphsonControl(maxit = 2, ...) inside
approximation_control. Exact refinement is required.
For spline terms, summary() reports monotonicity over the covariate
range at the focal sites, including the number of derivative sign changes.
A monotone curve increases or decreases throughout that range; a
nonmonotone curve changes direction. This describes the fitted curve, not
a confidence statement about its shape outside or within that range.
Gaussian scale-aware conductance.
gaussian_smoothed_loglinear_conductance can also use
approximation = "coarse_raster". In that case the Gaussian smoother is
rebuilt on each aggregated raster, while the full-resolution sigma bounds and
unit conversion are preserved so a warm-started sigma continues to
mean the same map-unit distance at every stage.
Currently, all of the built-in conductance models in terradish use the
default contrast coding (e.g. for a categorical covariate with K
factors, the estimated parameters are the K-1 mean differences
against a reference category). The intercept is excluded if it is not
identifiable.
If the resistance contribution is zero, or the formula specifies uniform conductance, conductance coefficients are not identified and their influence, gradient, and Hessian are unavailable. A zero environmental kernel weight does not by itself remove conductance inference.
Convergence and starting values. Inspect fit$convergence
before interpreting estimates. Code 0 requires a largest absolute projected
gradient below ctol, or an objective change below ftol together
with a projected gradient below sqrt(ctol). Code 1 means the iteration
limit was reached; code 2 means a stall or failed line search. The projected
gradient ignores components pointing outside active parameter bounds.
The record includes the criterion, iterations, gradient, boundary status,
and whether a restart was attempted. A small objective change alone is not
convergence. Also inspect fit$fit$subproblem$convergence for nuisance
optimization. Numerical convergence does not establish model adequacy.
If a user-supplied starting point reaches a no-structure boundary, fitting retries the default starting point and keeps the better likelihood. Report starting-value sensitivity when distinct solutions remain plausible.
An object of class c("terradish", "radish"), a list. Most
users need only the methods: summary() for estimates and intervals,
coef() and confint() for conductance coefficients,
conductance() and plot() for maps and curves, and
AIC(), anova(), or aic_table for comparison
(see terradish_methods). The list elements are:
call, formula
|
The matched call and the model formula. |
dim |
Named vector with the number of graph vertices (raster cells), focal sites, and graph edges. |
convergence |
List describing the conductance optimization:
|
fit |
The final graph and likelihood evaluation.
|
loglik, df, aic
|
Maximized log-likelihood, number of estimated
parameters (conductance coefficients plus nuisance parameters), and
|
mle |
Conductance estimates ( |
submodels |
The conductance-model and measurement-model functions
used for the fit; |
comparison |
Measurement family, |
cost, diagnostics, approximation
|
Newton steps and function calls, solver timing and evaluation diagnostics, and a record of any landmark or coarse-raster warm start. |
gaussian_scale_info, spline_monotonicity
|
Metadata for Gaussian
smoothing and spline conductance terms, or |
leverage |
Leverage arrays for the response ( |
storage |
Present only when |
For Wishart likelihoods, nu scales the log likelihood. Point
estimates do not depend on it, but standard errors, likelihood-ratio tests,
and information criteria do. It is a dispersion parameter that you set,
rather than the number of markers. Linkage and the population history
shared by loci can make effective information much smaller than the SNP
count. In forward-time simulations used to validate terradish, standard
errors at the SNP count were three to five times smaller than the spread
among replicate histories. Those model-based errors understated uncertainty.
Use terradish_cv_folds to compare conductance formulas, and
terradish_rescale_nu to report inference across plausible
values. For the same successful folds and nuisance settings, changing
nu rescales Wishart CV scores without changing their ranking.
Neither procedure identifies the true effective information or repairs
an unsuitable response matrix.
library(terra) data(melip) melip.altitude <- terra::unwrap(melip.altitude) melip.forestcover <- terra::unwrap(melip.forestcover) melip.coords <- terra::unwrap(melip.coords) # scaling spatial covariates helps avoid numeric overflow covariates <- c(terra::scale(melip.altitude), terra::scale(melip.forestcover)) names(covariates) <- c("altitude", "forestcover") # create parameterized conductance surface surface <- conductance_surface(covariates, melip.coords, directions = 8) fit_nnls <- terradish(melip.Fst ~ altitude * forestcover, data = surface, terradish::loglinear_conductance, terradish::leastsquares) summary(fit_nnls) # a different "measurement_model" that incorporates dependence # among pairwise measurements fit_mlpe <- terradish(melip.Fst ~ altitude + forestcover, data = surface, terradish::loglinear_conductance, terradish::mlpe) summary(fit_mlpe) # conductance surface with 95% CI fitted_conductance <- conductance(surface, fit_mlpe, quantile = 0.95) # test for an interaction using a likelihood ratio test fit_mlpe_interaction <- terradish(melip.Fst ~ forestcover * altitude, data = surface, terradish::loglinear_conductance, terradish::mlpe) anova(fit_mlpe, fit_mlpe_interaction) # test against null model of IBD using a LRT fit_mlpe_ibd <- terradish(melip.Fst ~ 1, data = surface, terradish::loglinear_conductance, terradish::mlpe) anova(fit_mlpe, fit_mlpe_ibd) # categorical covariates: # categorical raster layers should be factor-valued, see ?terra::as.factor # and 'details' section of ?conductance_surface forestcover_class <- cut(terra::values(melip.forestcover)[,1], breaks = c(0, 1/6, 1/3, 1)) melip.forestcover_cat <- terra::setValues(melip.forestcover, as.numeric(forestcover_class)) melip.forestcover_cat <- terra::as.factor(melip.forestcover_cat) RAT <- levels(melip.forestcover_cat)[[1]] RAT$VALUE <- levels(forestcover_class) #explicitly define level names levels(melip.forestcover_cat) <- RAT covariates_cat <- c(melip.forestcover_cat, melip.altitude) names(covariates_cat) <- c("forestcover", "altitude") surface_cat <- conductance_surface(covariates_cat, melip.coords, directions = 8) fit_mlpe_cat <- terradish(melip.Fst ~ forestcover + altitude, surface_cat, terradish::loglinear_conductance, terradish::mlpe) summary(fit_mlpe_cat)library(terra) data(melip) melip.altitude <- terra::unwrap(melip.altitude) melip.forestcover <- terra::unwrap(melip.forestcover) melip.coords <- terra::unwrap(melip.coords) # scaling spatial covariates helps avoid numeric overflow covariates <- c(terra::scale(melip.altitude), terra::scale(melip.forestcover)) names(covariates) <- c("altitude", "forestcover") # create parameterized conductance surface surface <- conductance_surface(covariates, melip.coords, directions = 8) fit_nnls <- terradish(melip.Fst ~ altitude * forestcover, data = surface, terradish::loglinear_conductance, terradish::leastsquares) summary(fit_nnls) # a different "measurement_model" that incorporates dependence # among pairwise measurements fit_mlpe <- terradish(melip.Fst ~ altitude + forestcover, data = surface, terradish::loglinear_conductance, terradish::mlpe) summary(fit_mlpe) # conductance surface with 95% CI fitted_conductance <- conductance(surface, fit_mlpe, quantile = 0.95) # test for an interaction using a likelihood ratio test fit_mlpe_interaction <- terradish(melip.Fst ~ forestcover * altitude, data = surface, terradish::loglinear_conductance, terradish::mlpe) anova(fit_mlpe, fit_mlpe_interaction) # test against null model of IBD using a LRT fit_mlpe_ibd <- terradish(melip.Fst ~ 1, data = surface, terradish::loglinear_conductance, terradish::mlpe) anova(fit_mlpe, fit_mlpe_ibd) # categorical covariates: # categorical raster layers should be factor-valued, see ?terra::as.factor # and 'details' section of ?conductance_surface forestcover_class <- cut(terra::values(melip.forestcover)[,1], breaks = c(0, 1/6, 1/3, 1)) melip.forestcover_cat <- terra::setValues(melip.forestcover, as.numeric(forestcover_class)) melip.forestcover_cat <- terra::as.factor(melip.forestcover_cat) RAT <- levels(melip.forestcover_cat)[[1]] RAT$VALUE <- levels(forestcover_class) #explicitly define level names levels(melip.forestcover_cat) <- RAT covariates_cat <- c(melip.forestcover_cat, melip.altitude) names(covariates_cat) <- c("forestcover", "altitude") surface_cat <- conductance_surface(covariates_cat, melip.coords, directions = 8) fit_mlpe_cat <- terradish(melip.Fst ~ forestcover + altitude, surface_cat, terradish::loglinear_conductance, terradish::mlpe) summary(fit_mlpe_cat)
Calculates likelihood, gradient, hessian, and partial derivatives of a parameterized conductance surface, given a function mapping spatial data to conductance and a function mapping resistance distance (covariance) to genetic distance; using the algorithm in Pope (in prep).
terradish_algorithm( f, g, s, S, theta, nu = NULL, phi = NULL, objective = TRUE, gradient = TRUE, hessian = TRUE, partial = TRUE, nonnegative = TRUE, validate = FALSE, cores = 1L, curvature = c("exact", "gauss_newton"), solver = c("direct", "auto", "amg"), solver_control = NULL, solver_warm_start = NULL, solver_reuse_state = NULL, measurement_control = NULL, worker_pool = NULL )terradish_algorithm( f, g, s, S, theta, nu = NULL, phi = NULL, objective = TRUE, gradient = TRUE, hessian = TRUE, partial = TRUE, nonnegative = TRUE, validate = FALSE, cores = 1L, curvature = c("exact", "gauss_newton"), solver = c("direct", "auto", "amg"), solver_control = NULL, solver_warm_start = NULL, solver_reuse_state = NULL, measurement_control = NULL, worker_pool = NULL )
f |
A function of class 'conductance_model' |
g |
A function of class 'measurement_model' |
s |
An object of class |
S |
A matrix of observed genetic distances |
theta |
Parameters for conductance surface (e.g. inputs to 'f') |
nu |
Effective Wishart degrees of freedom, passed through to measurement models that require it. |
phi |
Optional warm-start values for the nuisance-parameter subproblem. |
objective |
Compute negative loglikelihood? |
gradient |
Compute gradient of negative loglikelihood wrt theta? |
hessian |
Compute Hessian matrix of negative loglikelihood wrt theta? |
partial |
Compute partial derivatives of negative loglikelihood wrt theta and spatial covariates/observed genetic distances |
nonnegative |
Force regression-like 'measurement_model' to have nonnegative slope? |
validate |
Numerical validation via 'numDeriv' (very slow, use for debugging small examples) |
cores |
Number of worker processes to use for per-parameter derivative calculations. |
curvature |
Which curvature to return in |
solver |
Linear-system solver. |
solver_control |
Optional named list of solver settings. For |
solver_warm_start |
Optional initial guess for the reduced-system solve. This is primarily useful for iterative solvers when evaluating nearby parameter values. |
solver_reuse_state |
Optional reusable solver state returned by a prior
|
measurement_control |
Optional |
worker_pool |
Optional reusable worker pool created internally by
|
A list containing at a minimum:
covariance |
rows/columns of the generalized inverse of the graph Laplacian for a subset of target vertices |
Additionally, if 'objective == TRUE':
objective |
(if 'objective') the negative loglikelihood |
phi |
(if 'objective') fitted values of the nuisance parameters of 'g' |
subproblem |
(if 'objective') convergence code and iteration count for the nuisance-parameter profile |
boundary |
(if 'objective') is the solution on the boundary (e.g. no genetic structure)? |
fitted |
(if 'objective') matrix of expected genetic distances among target vertices |
gradient |
(if 'gradient') gradient of negative loglikelihood with respect to theta |
hessian |
(if 'hessian') Hessian matrix of the negative loglikelihood with respect to theta |
partial_X |
(if 'partial') Jacobian of the gradient with respect to the spatial covariates |
partial_S |
(if 'partial') Jacobian of the gradient with respect to the observed genetic distances |
library(terra) data(melip) melip.altitude <- terra::unwrap(melip.altitude) melip.forestcover <- terra::unwrap(melip.forestcover) melip.coords <- terra::unwrap(melip.coords) covariates <- c(melip.altitude, melip.forestcover) names(covariates) <- c("altitude", "forestcover") surface <- conductance_surface(covariates, melip.coords, directions = 8) conductance_model <- loglinear_conductance(~ altitude + forestcover, surface$x) terradish_algorithm(conductance_model, terradish::leastsquares, surface, melip.Fst, theta = c(-0.3, 0.3))library(terra) data(melip) melip.altitude <- terra::unwrap(melip.altitude) melip.forestcover <- terra::unwrap(melip.forestcover) melip.coords <- terra::unwrap(melip.coords) covariates <- c(melip.altitude, melip.forestcover) names(covariates) <- c("altitude", "forestcover") surface <- conductance_surface(covariates, melip.coords, directions = 8) conductance_model <- loglinear_conductance(~ altitude + forestcover, surface$x) terradish_algorithm(conductance_model, terradish::leastsquares, surface, melip.Fst, theta = c(-0.3, 0.3))
Profiles a terradish graph and, optionally, runs short probe
benchmarks to recommend optimizer, line-search, solver, and approximation
settings for a fitted model. Use this function before a large or slow fit
to identify the fastest computational configuration for your data.
terradish_assess_settings( formula, data, conductance_model = loglinear_conductance, measurement_model = mlpe, nu = NULL, theta = NULL, nonnegative = TRUE, optimizer_probe = TRUE, solver_probe = TRUE, coarse_probe = FALSE, optimizer_candidates = c("newton_hager_zhang", "bfgs_hager_zhang", "bfgs_armijo"), probe_maxit = 2L, factorization = c("simplicial_ldl", "simplicial_ll", "supernodal_ll"), solve_backend = c("matrix", "cholmod_cpp", "cholmod_cpp_cached"), coarse_factor = 2L, coarse_refine_maxit = 1L, loglik_tolerance = 0.001, max_seconds = 120, cores = 1L, verbose = FALSE )terradish_assess_settings( formula, data, conductance_model = loglinear_conductance, measurement_model = mlpe, nu = NULL, theta = NULL, nonnegative = TRUE, optimizer_probe = TRUE, solver_probe = TRUE, coarse_probe = FALSE, optimizer_candidates = c("newton_hager_zhang", "bfgs_hager_zhang", "bfgs_armijo"), probe_maxit = 2L, factorization = c("simplicial_ldl", "simplicial_ll", "supernodal_ll"), solve_backend = c("matrix", "cholmod_cpp", "cholmod_cpp_cached"), coarse_factor = 2L, coarse_refine_maxit = 1L, loglik_tolerance = 0.001, max_seconds = 120, cores = 1L, verbose = FALSE )
formula |
Model formula passed to |
data |
A |
conductance_model |
Conductance-model factory (e.g.
|
measurement_model |
Measurement model used during optimizer probes. Should match the model you plan to use for the final fit. |
nu |
Effective Wishart degrees of freedom passed to measurement models
that require it ( |
theta |
Optional starting conductance parameter values. If
|
nonnegative |
Should regression measurement models constrain the IBR slope to be nonnegative during probes? |
optimizer_probe |
Logical. Run short optimization probes to compare Newton-Raphson vs. BFGS and line-search algorithms? |
solver_probe |
Logical. Benchmark direct Cholesky solver settings
(factorization style and solve backend) via
|
coarse_probe |
Logical. Include a coarse-raster warm-start probe?
Only applicable when |
optimizer_candidates |
Character vector of optimizer configurations to
probe. Supported entries:
|
probe_maxit |
Maximum optimizer steps per probe. Keep small (default
|
factorization |
Character vector of Cholesky factorization styles to
benchmark when |
solve_backend |
Character vector of solve backends to benchmark when
|
coarse_factor |
Raster aggregation factor for the coarse-raster probe
(passed to the |
coarse_refine_maxit |
Maximum full-resolution refinement steps for the coarse-raster probe. |
loglik_tolerance |
Maximum log-likelihood difference below the best probe that is still considered acceptable when choosing the fastest option. |
max_seconds |
Soft wall-time budget in seconds. Each probe that starts before the budget is exceeded is allowed to finish; later probes are skipped. |
cores |
Number of parallel worker processes passed to probes. |
verbose |
Logical. Print progress as probes run? |
terradish_assess_settings is an advisory tool, not a model-selection
procedure. It does not change package defaults and its output does not
affect parameter estimation. Use it to choose computational
settings; then fit and interpret your model as usual.
It is a speed probe. A fast candidate is not evidence of optimizer
convergence, identification, correct standard errors, or biological validity.
Short probes work well for comparing early-iteration cost of different optimizers. They are most informative for large graphs (> 100,000 vertices) where a single function evaluation is slow. For small graphs the default Newton-Raphson + Hager-Zhang settings typically converge in seconds, and assessment adds more overhead than it saves.
The returned $recommended list is intentionally a plain named list of
arguments (optimizer, control, solver,
solver_control, approximation,
approximation_control). You can inspect, edit, or pass it directly:
fit <- terradish(formula, data,
optimizer = assessment$recommended$optimizer,
control = assessment$recommended$control,
solver = assessment$recommended$solver)
An object of class "terradish_setting_assessment" with
components:
profileA list of graph statistics (number of vertices,
edges, focal sites, parameters, and, for
smooth_loglinear_conductance, spline-expansion counts.
optimizer_benchmarkA data frame of optimizer probe results
(one row per candidate), or NULL if optimizer_probe = FALSE.
solver_benchmarkA data frame of solver probe results, or
NULL if solver_probe = FALSE.
defaultsThe terradish defaults for the profiled model,
after resolving optimizer = "auto" on the expanded conductance
parameter count.
comparisonA compact comparison between the recommended settings and the terradish defaults, including a one-line summary.
recommendedA named list of recommended settings:
optimizer, control, solver, solver_control,
approximation, approximation_control.
notesCharacter vector of advisory messages.
A print method summarizes the profile and recommendations.
data(melip) melip.altitude <- terra::unwrap(melip.altitude) melip.forestcover <- terra::unwrap(melip.forestcover) melip.coords <- terra::unwrap(melip.coords) covariates <- c(melip.altitude, melip.forestcover) names(covariates) <- c("altitude", "forestcover") # Coarsened for the example. Assessment is most useful on the large graphs # where solver choice actually matters, so use the full resolution there. covariates <- terra::aggregate(covariates, fact = 3, na.rm = TRUE) covariates <- scale_covariates(covariates) surface <- conductance_surface(covariates, melip.coords, directions = 8, saveStack = TRUE) assessment <- terradish_assess_settings( melip.Fst ~ forestcover + altitude, data = surface, measurement_model = mlpe, probe_maxit = 2 ) assessment fit <- terradish( melip.Fst ~ forestcover + altitude, data = surface, conductance_model = loglinear_conductance, measurement_model = mlpe, optimizer = assessment$recommended$optimizer, control = assessment$recommended$control, solver = assessment$recommended$solver, solver_control = assessment$recommended$solver_control ) smooth_assessment <- terradish_assess_settings( melip.Fst ~ forestcover + s(altitude, df = 3), data = surface, conductance_model = smooth_loglinear_conductance, measurement_model = mlpe, probe_maxit = 1 ) smooth_assessment$comparison$summarydata(melip) melip.altitude <- terra::unwrap(melip.altitude) melip.forestcover <- terra::unwrap(melip.forestcover) melip.coords <- terra::unwrap(melip.coords) covariates <- c(melip.altitude, melip.forestcover) names(covariates) <- c("altitude", "forestcover") # Coarsened for the example. Assessment is most useful on the large graphs # where solver choice actually matters, so use the full resolution there. covariates <- terra::aggregate(covariates, fact = 3, na.rm = TRUE) covariates <- scale_covariates(covariates) surface <- conductance_surface(covariates, melip.coords, directions = 8, saveStack = TRUE) assessment <- terradish_assess_settings( melip.Fst ~ forestcover + altitude, data = surface, measurement_model = mlpe, probe_maxit = 2 ) assessment fit <- terradish( melip.Fst ~ forestcover + altitude, data = surface, conductance_model = loglinear_conductance, measurement_model = mlpe, optimizer = assessment$recommended$optimizer, control = assessment$recommended$control, solver = assessment$recommended$solver, solver_control = assessment$recommended$solver_control ) smooth_assessment <- terradish_assess_settings( melip.Fst ~ forestcover + s(altitude, df = 3), data = surface, conductance_model = smooth_loglinear_conductance, measurement_model = mlpe, probe_maxit = 1 ) smooth_assessment$comparison$summary
Functions that generate objects of class "terradish_conductance_model"
that represent mappings from spatial data (e.g. rasters) to conductance.
terradish_conductance_model_factoryterradish_conductance_model_factory
See vignette("getting-started", package = "terradish") for a
log-linear model, vignette("categorical-conductance", package = "terradish")
for class contrasts, and vignette("spline-conductance", package = "terradish")
for nonlinear curves and their plots.
loglinear_conductance,
smooth_loglinear_conductance
Fits one or more conductance formulas to each training fold while retaining the complete landscape graph, then evaluates the trained coefficients on the held-out focal sites. Measurement parameters can be reprofiled on each test fold or held at their training estimates for predictive scoring.
terradish_cv_folds( data, formulas, folds, model = "mlpe", nu = NULL, conductance_model = loglinear_conductance, baseline = TRUE, keep_fits = c("none", "slim", "full"), checkpoint = NULL, resume = TRUE, cores = 1L, measurement_control = NULL, nuisance = c("reprofile", "fixed"), ... )terradish_cv_folds( data, formulas, folds, model = "mlpe", nu = NULL, conductance_model = loglinear_conductance, baseline = TRUE, keep_fits = c("none", "slim", "full"), checkpoint = NULL, resume = TRUE, cores = 1L, measurement_control = NULL, nuisance = c("reprofile", "fixed"), ... )
data |
A prebuilt |
formulas |
A formula or named list of formulas. All formulas must use the same response matrix and likelihood family. |
folds |
Fold membership for each focal site. Use
|
model |
Measurement model function or one of |
nu |
Effective Wishart degrees of freedom when required by the selected measurement model. |
conductance_model |
One conductance-model factory or a named list matching the formula names, allowing log-linear, Gaussian, and spline models in the same comparison. |
baseline |
Should each test fold also be scored at zero conductance coefficients, the uniform-conductance baseline on the same graph? |
keep_fits |
Whether to retain no training fits, slim fits, or full fits. |
checkpoint |
Optional file path. Completed fold results are written atomically after every successful or failed fit so a long run can be inspected or resumed. |
resume |
Logical. If |
cores |
Number of workers used inside each model fit. Fold evaluation is serial to avoid nested parallel clusters. |
measurement_control |
Optional |
nuisance |
|
... |
Additional arguments passed to |
The graph domain and raster covariates remain fixed. Only the focal-site
columns in demes and rhs, the response matrix, and any
measurement-model site attributes are subset. This evaluates transfer to
held-out sites within the observed landscape rather than rebuilding a new
landscape for every fold.
Reprofiled scores compare conductance formulas under one measurement model;
they must not be compared across calls with different measurement models or
effective degrees of freedom. Fixed-parameter scores are predictive marginal
densities and can compare measurement models within one likelihood family.
Neither mode scores pairs spanning training and test sites. Spatial folds
score only within-cluster pairs. Wishart score gains scale with nu,
although ideal fitted rankings are invariant to its scale.
Checkpoints include a signature of the response, graph, focal cells,
covariates, model definitions, formulas, nu, and fold assignments.
For factor covariates the signature retains class placement, levels, and
contrasts. Uniform ~1 formulas are scored with unit conductance.
A mismatched signature stops resumption. Nonconverged fits and failed scores
count as failed folds. Training fits must record code 0 for both outer and
final nuisance optimization; missing diagnostics also fail the fold.
Checkpoints predating this convergence requirement are incompatible and
must be recomputed (use a new checkpoint or resume = FALSE).
With fixed nuisance parameters, no-structure boundary
fits remain scoreable because predictions do not depend on conductance.
A failed uniform-baseline score is recorded separately: model scores remain
usable, but baseline gains are unavailable for that fold.
Models with failed folds receive
NA totals and an explicit success count. Rankings use only folds successful
for every model. Paired differences and their standard errors are descriptive;
repeated folds share observations and are not independent replicates.
loglik_gain is the trained model's held-out log-likelihood minus the
uniform-conductance baseline log-likelihood. Larger values indicate improved
prediction over that baseline. Raw log-likelihood and gain are comparable
only among models using the same response representation, measurement
likelihood, folds, graph, and effective degrees of freedom. They are not a
common scoring rule for comparing MLPE, generalized-Wishart distance, and
covariance-Wishart models.
An object of class "terradish_cv_folds". Its folds
element records the site assignments, results contains one row per
formula and fold, and summary reports summed and mean held-out scores
by formula, common-fold totals, paired differences from the best model,
the standard error of their mean, and a one-standard-error flag.
Repeated runs also report the SD of repeat totals over common folds.
Optional retained fits are stored in fits.
See vignette("model-comparison", package = "terradish") for pond
comparisons and vignette("individual-salamanders", package = "terradish")
for individual sampling, matched markers, and spatial prediction plots.
r <- terra::rast(nrows = 8, ncols = 8, xmin = 0, xmax = 8, ymin = 0, ymax = 8, crs = "EPSG:32617") terra::values(r) <- sin(seq_len(64) / 5) + seq_len(64) / 64 names(r) <- "x" sites <- terra::xyFromCell(r, c(1, 5, 8, 12, 20, 27, 33, 37, 45, 53, 57, 64)) surface <- conductance_surface(r, sites) response <- simulate_covariance_response(c(x = 0.8), ~x, surface, nu = 1000, seed = 93)$covariance folds <- terradish_folds(sites, k = 3, method = "random", seed = 1) cv <- terradish_cv_folds( surface, list(uniform = response ~ 1, gradient = response ~ x), folds = folds, model = wishart_covariance, nu = 1000, nuisance = "fixed", control = NewtonRaphsonControl(maxit = 100)) cv$summary # Check fold success counts before comparing common-fold predictive scores. # Random folds illustrate the call; choose ecological spatial blocks in practice.r <- terra::rast(nrows = 8, ncols = 8, xmin = 0, xmax = 8, ymin = 0, ymax = 8, crs = "EPSG:32617") terra::values(r) <- sin(seq_len(64) / 5) + seq_len(64) / 64 names(r) <- "x" sites <- terra::xyFromCell(r, c(1, 5, 8, 12, 20, 27, 33, 37, 45, 53, 57, 64)) surface <- conductance_surface(r, sites) response <- simulate_covariance_response(c(x = 0.8), ~x, surface, nu = 1000, seed = 93)$covariance folds <- terradish_folds(sites, k = 3, method = "random", seed = 1) cv <- terradish_cv_folds( surface, list(uniform = response ~ 1, gradient = response ~ x), folds = folds, model = wishart_covariance, nu = 1000, nuisance = "fixed", control = NewtonRaphsonControl(maxit = 100)) cv$summary # Check fold success counts before comparing common-fold predictive scores. # Random folds illustrate the call; choose ecological spatial blocks in practice.
Calculates resistance distances associated with a parameterized conductance surface across a grid of parameter values.
terradish_distance( theta, formula, data, conductance_model = loglinear_conductance, conductance = TRUE, covariance = FALSE, cores = 1L )terradish_distance( theta, formula, data, conductance_model = loglinear_conductance, conductance = TRUE, covariance = FALSE, cores = 1L )
theta |
A matrix of dimension (grid size) x (number of parameters) |
formula |
A formula with the name of a matrix of observed genetic distances on the lhs, and covariates in the creation of |
data |
An object of class |
conductance_model |
A function of class
|
conductance |
Retained for backward compatibility. Only
|
covariance |
If |
cores |
Number of worker processes to use. |
A list of class c("terradish_grid", "radish_grid"):
theta |
Data frame of the evaluated conductance coefficients, one row per grid point. |
distance |
When |
covariance |
When |
library(terra) data(melip) melip.altitude <- terra::unwrap(melip.altitude) melip.forestcover <- terra::unwrap(melip.forestcover) melip.coords <- terra::unwrap(melip.coords) keep <- 1:10 covariates <- terra::aggregate(c(melip.altitude, melip.forestcover), fact = 5, na.rm = TRUE) covariates <- scale_covariates(covariates) names(covariates) <- c("altitude", "forestcover") surface <- conductance_surface(covariates, melip.coords[keep], directions = 8) theta <- as.matrix(expand.grid(forestcover = seq(-0.5, 0.5, length.out = 3), altitude = seq(-0.5, 0.5, length.out = 3))) distances <- terradish_distance(theta, ~forestcover + altitude, surface, terradish::loglinear_conductance, cores = 1) ibd <- which(theta[,1] == 0 & theta[,2] == 0) round(distances$distance[,,ibd], 4)library(terra) data(melip) melip.altitude <- terra::unwrap(melip.altitude) melip.forestcover <- terra::unwrap(melip.forestcover) melip.coords <- terra::unwrap(melip.coords) keep <- 1:10 covariates <- terra::aggregate(c(melip.altitude, melip.forestcover), fact = 5, na.rm = TRUE) covariates <- scale_covariates(covariates) names(covariates) <- c("altitude", "forestcover") surface <- conductance_surface(covariates, melip.coords[keep], directions = 8) theta <- as.matrix(expand.grid(forestcover = seq(-0.5, 0.5, length.out = 3), altitude = seq(-0.5, 0.5, length.out = 3))) distances <- terradish_distance(theta, ~forestcover + altitude, surface, terradish::loglinear_conductance, cores = 1) ibd <- which(theta[,1] == 0 & theta[,2] == 0) round(distances$distance[,,ibd], 4)
Creates either spatially blocked folds by k-means clustering projected site
coordinates or balanced random folds. Fold labels can also be supplied
directly to terradish_cv_folds when study-specific blocks are
more defensible than an automated partition.
terradish_folds( pts, k = 5L, method = c("spatial_kmeans", "random"), seed = 1L, projected = NULL, nstart = 25L )terradish_folds( pts, k = 5L, method = c("spatial_kmeans", "random"), seed = 1L, projected = NULL, nstart = 25L )
pts |
Focal-site coordinates as a |
k |
Number of folds. Must be at least 2 and smaller than the number of sites. |
method |
Fold construction method. |
seed |
Optional random seed. |
projected |
Logical indicating whether the coordinates are projected.
For spatial k-means with a |
nstart |
Number of random starts used by k-means. |
Coordinates are deliberately not standardized before spatial clustering. Consequently, Euclidean separation is measured in the map units of the projected coordinate reference system. Automated k-means blocks are a reproducible diagnostic, not a substitute for ecological blocking based on watersheds, barriers, sampling campaigns, or other design information.
An integer vector of fold labels with attributes recording the construction method, seed, and number of folds.
data(melip) coords <- terra::unwrap(melip.coords) coords_projected <- terra::project(coords, "EPSG:5070") folds <- terradish_folds(coords_projected, k = 3, seed = 1) table(folds)data(melip) coords <- terra::unwrap(melip.coords) coords_projected <- terra::project(coords, "EPSG:5070") folds <- terradish_folds(coords_projected, k = 3, seed = 1) table(folds)
Calculates the profile likelihood of a parameterized conductance surface across a grid of parameter values (e.g. the nuisance parameters are optimized at each point on the grid).
terradish_grid( theta, formula, data, conductance_model = loglinear_conductance, measurement_model = mlpe, nu = NULL, nonnegative = TRUE, conductance = TRUE, covariance = FALSE, cores = 1L, approximation = "none", approximation_control = NULL )terradish_grid( theta, formula, data, conductance_model = loglinear_conductance, measurement_model = mlpe, nu = NULL, nonnegative = TRUE, conductance = TRUE, covariance = FALSE, cores = 1L, approximation = "none", approximation_control = NULL )
theta |
A matrix of dimension (grid size) x (number of parameters) |
formula |
A formula with the name of a response matrix compatible with
the selected measurement model on the left-hand side, and covariates used
to create |
data |
An object of class |
conductance_model |
A function of class
|
measurement_model |
A function of class
|
nu |
Effective Wishart degrees of freedom passed to measurement models
that require it. This is a user-supplied effective-information parameter,
not the marker count. Report sensitivity to plausible values; see
|
nonnegative |
Force regression-like |
conductance |
Retained for backward compatibility. Only
|
covariance |
If |
cores |
Number of worker processes to use. |
approximation |
Only |
approximation_control |
Reserved for compatibility; unused. |
A list of class c("terradish_grid", "radish_grid"), with one
entry per row of theta:
theta |
Data frame of the evaluated conductance coefficients (log conductance per unit of each covariate). |
loglik |
Numeric vector of profile log-likelihoods, with nuisance parameters optimized at each grid point. Larger values indicate better fit, and differences between grid points are on the log-likelihood scale. |
phi |
Matrix of optimized nuisance parameters, one column per grid point, on the measurement model's internal scale. |
covariance |
When |
approximation |
Record of the approximation setting (always
|
library(terra) data(melip) melip.altitude <- terra::unwrap(melip.altitude) melip.forestcover <- terra::unwrap(melip.forestcover) melip.coords <- terra::unwrap(melip.coords) covariates <- c(terra::scale(melip.altitude), terra::scale(melip.forestcover)) names(covariates) <- c("altitude", "forestcover") surface <- conductance_surface(covariates, melip.coords, directions = 8, saveStack = TRUE) fit_mlpe <- terradish(melip.Fst ~ altitude + forestcover, data = surface, terradish::loglinear_conductance, terradish::mlpe, control = NewtonRaphsonControl(maxit = 5, verbose = FALSE)) theta <- as.matrix(expand.grid(forestcover = seq(-0.5, 0.5, length.out = 3), altitude = seq(-0.5, 0.5, length.out = 3))) grid <- terradish_grid(theta, melip.Fst ~ forestcover + altitude, surface, terradish::loglinear_conductance, terradish::mlpe, cores = 1) cbind(grid$theta, loglik = grid$loglik) coef(fit_mlpe)library(terra) data(melip) melip.altitude <- terra::unwrap(melip.altitude) melip.forestcover <- terra::unwrap(melip.forestcover) melip.coords <- terra::unwrap(melip.coords) covariates <- c(terra::scale(melip.altitude), terra::scale(melip.forestcover)) names(covariates) <- c("altitude", "forestcover") surface <- conductance_surface(covariates, melip.coords, directions = 8, saveStack = TRUE) fit_mlpe <- terradish(melip.Fst ~ altitude + forestcover, data = surface, terradish::loglinear_conductance, terradish::mlpe, control = NewtonRaphsonControl(maxit = 5, verbose = FALSE)) theta <- as.matrix(expand.grid(forestcover = seq(-0.5, 0.5, length.out = 3), altitude = seq(-0.5, 0.5, length.out = 3))) grid <- terradish_grid(theta, melip.Fst ~ forestcover + altitude, surface, terradish::loglinear_conductance, terradish::mlpe, cores = 1) cbind(grid$theta, loglik = grid$loglik) coef(fit_mlpe)
Reports the resistance-distance equivalent of one unit of environmental difference, using the environmental coefficient divided by the resistance coefficient in the additive measurement model.
terradish_ibe_ratio(fit)terradish_ibe_ratio(fit)
fit |
A fitted model from |
The ratios are for MLPE and
for Wishart models. They require the same environmental
transform and scaling for comparison. Their interpretation agrees exactly
for the same covariance-derived distance and approximately for distances
that scale linearly with it. Separate fits estimate their own conductance
surfaces, so their resistance distances can differ. Absolute measurement
coefficients are not comparable across families.
A zero resistance coefficient gives undefined ratios and uncertainty, returned as NA. A zero Wishart kernel coefficient receives a one-sided upper limit and no symmetric lower limit. Boundary inference remains approximate. The distance-equivalent ratio interpretation also appears in BEDASSLE, although that model uses geographic rather than resistance distance.
A data frame with covariate, ratio, SE, lower, upper, and family. Intervals use a 95 percent delta-method approximation based on the joint covariance of both coefficients, including fitted-conductance uncertainty. Fits without environmental terms return a zero-row data frame.
Bradburd GS, Ralph PL, Coop GM (2013). Disentangling the effects of geographic and ecological isolation on genetic differentiation. Evolution, 67, 3258–3273. doi:10.1111/evo.12193.
pairwise_covariates, mlpe_covariates,
wishart_covariates, terradish_rescale_nu
# A small model-based example, with fixed information supplied explicitly. r <- terra::rast(nrows = 8, ncols = 8, xmin = 0, xmax = 8, ymin = 0, ymax = 8) terra::values(r) <- sin(seq_len(64) / 5) + seq_len(64) / 64 names(r) <- "x" sites <- terra::xyFromCell(r, c(1, 8, 20, 37, 57, 64)) graph <- conductance_surface(r, sites) simulated <- simulate_covariance_response(c(x = 0.6), ~x, graph, nu = 1000, seed = 93) response <- simulated$covariance fit <- terradish(response ~ x, graph, measurement_model = wishart_covariance, nu = 1000) environment <- pairwise_covariates( pairwise_endpoint_covariates(data.frame(climate = c(2, 4, 1, 7, 5, 9)))) measurement <- wishart_covariates(environment, model = "wishart_covariance") kernel <- attr(measurement, "kernel_covariates")[, , 1] response <- response + 0.1 * kernel joint <- terradish(response ~ x, graph, measurement_model = measurement, nu = 1000) terradish_ibe_ratio(joint) # Each ratio is the resistance-distance equivalent of one unit of difference.# A small model-based example, with fixed information supplied explicitly. r <- terra::rast(nrows = 8, ncols = 8, xmin = 0, xmax = 8, ymin = 0, ymax = 8) terra::values(r) <- sin(seq_len(64) / 5) + seq_len(64) / 64 names(r) <- "x" sites <- terra::xyFromCell(r, c(1, 8, 20, 37, 57, 64)) graph <- conductance_surface(r, sites) simulated <- simulate_covariance_response(c(x = 0.6), ~x, graph, nu = 1000, seed = 93) response <- simulated$covariance fit <- terradish(response ~ x, graph, measurement_model = wishart_covariance, nu = 1000) environment <- pairwise_covariates( pairwise_endpoint_covariates(data.frame(climate = c(2, 4, 1, 7, 5, 9)))) measurement <- wishart_covariates(environment, model = "wishart_covariance") kernel <- attr(measurement, "kernel_covariates")[, , 1] response <- response + 0.1 * kernel joint <- terradish(response ~ x, graph, measurement_model = measurement, nu = 1000) terradish_ibe_ratio(joint) # Each ratio is the resistance-distance equivalent of one unit of difference.
Extracts observed-curvature uncertainty from a full or slim fit.
## S3 method for class 'terradish' vcov(object, ...) ## S3 method for class 'terradish' nobs(object, ...) ## S3 method for class 'terradish' confint(object, parm, level = 0.95, ...)## S3 method for class 'terradish' vcov(object, ...) ## S3 method for class 'terradish' nobs(object, ...) ## S3 method for class 'terradish' confint(object, parm, level = 0.95, ...)
object |
A fitted |
... |
Unused arguments for generic compatibility. |
parm |
Names or indices of parameters to include. |
level |
Confidence level between zero and one. |
Gaussian scale intervals are Wald intervals truncated at the fitted bounds.
Stored profile intervals take precedence when their confidence level matches.
The observation count is the number of focal sites (individuals or
populations), the sample size that AICc and BIC use in
aic_table. Pairwise observations are not counted, because
pairs that share a site are not independent.
vcov() returns a covariance matrix; confint() returns
a two-column interval matrix; nobs() returns the number of focal sites.
terradish, slim_terradish,
gaussian_scale_profile, terradish_rescale_nu
# A small model-based example, with fixed information supplied explicitly. r <- terra::rast(nrows = 8, ncols = 8, xmin = 0, xmax = 8, ymin = 0, ymax = 8) terra::values(r) <- sin(seq_len(64) / 5) + seq_len(64) / 64 names(r) <- "x" sites <- terra::xyFromCell(r, c(1, 8, 20, 37, 57, 64)) graph <- conductance_surface(r, sites) simulated <- simulate_covariance_response(c(x = 0.6), ~x, graph, nu = 1000, seed = 93) response <- simulated$covariance fit <- terradish(response ~ x, graph, measurement_model = wishart_covariance, nu = 1000) coef(fit) # Relative log conductance per unit of x. vcov(fit) # Joint uncertainty in conductance parameters. confint(fit) # A model-based interval conditional on the supplied nu. nobs(fit) # Number of focal sites, the sample size for AICc and BIC.# A small model-based example, with fixed information supplied explicitly. r <- terra::rast(nrows = 8, ncols = 8, xmin = 0, xmax = 8, ymin = 0, ymax = 8) terra::values(r) <- sin(seq_len(64) / 5) + seq_len(64) / 64 names(r) <- "x" sites <- terra::xyFromCell(r, c(1, 8, 20, 37, 57, 64)) graph <- conductance_surface(r, sites) simulated <- simulate_covariance_response(c(x = 0.6), ~x, graph, nu = 1000, seed = 93) response <- simulated$covariance fit <- terradish(response ~ x, graph, measurement_model = wishart_covariance, nu = 1000) coef(fit) # Relative log conductance per unit of x. vcov(fit) # Joint uncertainty in conductance parameters. confint(fit) # A model-based interval conditional on the supplied nu. nobs(fit) # Number of focal sites, the sample size for AICc and BIC.
Functions that fit a matrix observed genetic distances to a submatrix of the inverse graph Laplacian
terradish_measurement_modelterradish_measurement_model
For the ordinary two-component wishart_covariance and
generalized_wishart models with their standard bounds,
nuisance fitting first profiles the overall covariance variance analytically
and searches the relative graph/nugget contribution on site contrasts.
This deterministic initialization avoids false convergence when a warm
log nugget has an almost zero derivative. The existing Newton optimizer
then refines the result and computes derivatives at the same parameters.
Fixed predictive parameters and custom bounds retain their supplied values
and constraints. The finite profile search does not guarantee a global
optimum for every possible covariance matrix. A nugget approaching zero
remains a limiting estimate and can imply weak nuisance identification.
leastsquares, mlpe, generalized_wishart
S3 methods for working with fitted objects returned by terradish
and summaries returned by summary().
## S3 method for class 'terradish' print(x, digits = max(3L, getOption("digits") - 3L), ...) ## S3 method for class 'terradish' summary(object, conf.level = 0.95, ...) ## S3 method for class 'summary.terradish' print( x, digits = max(3L, getOption("digits") - 3L), signif.stars = getOption("show.signif.stars"), ... ) ## S3 method for class 'terradish' coef(object, ...) ## S3 method for class 'terradish' fitted(object, type = c("response", "distance", "covariance"), ...) ## S3 method for class 'terradish' simulate( object, nsim = 1, seed = NULL, method = c("permutation", "parametric"), ... ) ## S3 method for class 'terradish' anova(object, ..., alternative = NULL) ## S3 method for class 'terradish' logLik(object, ...) ## S3 method for class 'terradish' AIC(object, ..., k = 2) ## S3 method for class 'terradish' residuals(object, ...)## S3 method for class 'terradish' print(x, digits = max(3L, getOption("digits") - 3L), ...) ## S3 method for class 'terradish' summary(object, conf.level = 0.95, ...) ## S3 method for class 'summary.terradish' print( x, digits = max(3L, getOption("digits") - 3L), signif.stars = getOption("show.signif.stars"), ... ) ## S3 method for class 'terradish' coef(object, ...) ## S3 method for class 'terradish' fitted(object, type = c("response", "distance", "covariance"), ...) ## S3 method for class 'terradish' simulate( object, nsim = 1, seed = NULL, method = c("permutation", "parametric"), ... ) ## S3 method for class 'terradish' anova(object, ..., alternative = NULL) ## S3 method for class 'terradish' logLik(object, ...) ## S3 method for class 'terradish' AIC(object, ..., k = 2) ## S3 method for class 'terradish' residuals(object, ...)
x |
A fitted |
digits |
Number of digits to print. |
... |
Additional arguments passed through to generic methods. |
object, alternative
|
Fitted |
conf.level |
Confidence level for nuisance-parameter confidence
intervals reported by |
signif.stars |
Should significance stars be printed in coefficient tables? |
type |
Type of fitted values to return. |
nsim |
Number of simulated response matrices to generate. |
seed |
Optional random seed used by |
method |
Simulation method. |
k |
Penalty multiplier supplied to |
Nuisance-parameter estimates and intervals from summary() remain on
the parameterization used by the measurement model. For example, MLPE
tau is log precision and its stored rho is unconstrained; the
actual shared-site correlation is . Consult the
selected measurement-model help page before interpreting phi.
For a verified numerical zero residual-variance limit in a Wishart fit,
summary() retains the finite log-scale representation of sigma
but suppresses its Wald standard error and interval. A zero residual variance
does not imply a zero graph contribution.
Covariance-response residuals are centered on both site dimensions:
, where is the site-centering matrix. Fitted
covariance matrices retain their full form.
anova() is a likelihood-ratio test for nested conductance formulas
fitted to the same response, sites, graph domain, conductance-model factory,
measurement model, and pair subset; Wishart fits must also use the same
nu. Both fits must record successful outer and final nuisance
convergence; missing diagnostics require refitting. The function checks
formula-term nesting, but the analyst must
still verify that the full measurement and conductance parameterization is
substantively nested. Its chi-squared reference distribution assumes the
tested parameters are regular interior parameters. It can be unreliable when
a null value is on a parameter boundary or other regularity conditions fail;
use a justified alternative or simulation calibration in those cases.
print() returns its input invisibly.
summary() returns an object of class
summary.terradish, with legacy summary.radish
compatibility retained.
coef() returns the fitted conductance coefficients.
fitted() returns fitted responses, distances, or covariance.
simulate() returns one or more simulated response matrices.
anova() returns a likelihood-ratio comparison table.
logLik() returns a logLik object.
AIC() returns ; the default k = 2
gives Akaike's Information Criterion.
residuals() returns the residual response matrix.
terradish for fitting, conductance for
the fitted surface as a raster, plot.terradish for the five
diagnostic plot types, aic_table for ranking a model set,
and mlpe_response_change for putting an MLPE covariate
association on the response scale. See
vignette("getting-started", package = "terradish") for a walkthrough
of reading these outputs and
vignette("model-comparison", package = "terradish") for
anova() and AIC() in context.
data(melip) covariates <- c(terra::unwrap(melip.altitude), terra::unwrap(melip.forestcover)) names(covariates) <- c("altitude", "forestcover") covariates <- scale_covariates(terra::aggregate(covariates, fact = 3, na.rm = TRUE)) surface <- conductance_surface(covariates, terra::unwrap(melip.coords), directions = 8) fit <- terradish(melip.Fst ~ altitude + forestcover, data = surface, conductance_model = loglinear_conductance, measurement_model = mlpe) fit # coefficients, log-likelihood, and AIC summary(fit) # adds standard errors, Wald tests, and the phi table # Conductance coefficients are on the LOG scale, so exponentiate for a # multiplicative reading: exp(theta) is the change in conductance per # one-unit (one standard deviation, here) increase in the covariate. coef(fit) exp(coef(fit)) logLik(fit) # carries a df attribute, so AIC()/BIC() work AIC(fit) # compare only fits using the same response and likelihood family head(fitted(fit)[, 1:4]) # fitted genetic distances head(residuals(fit)[, 1:4]) # observed minus fitted # A nested comparison: is forest cover worth its parameter? fit_alt <- terradish(melip.Fst ~ altitude, data = surface, conductance_model = loglinear_conductance, measurement_model = mlpe) anova(fit_alt, fit)data(melip) covariates <- c(terra::unwrap(melip.altitude), terra::unwrap(melip.forestcover)) names(covariates) <- c("altitude", "forestcover") covariates <- scale_covariates(terra::aggregate(covariates, fact = 3, na.rm = TRUE)) surface <- conductance_surface(covariates, terra::unwrap(melip.coords), directions = 8) fit <- terradish(melip.Fst ~ altitude + forestcover, data = surface, conductance_model = loglinear_conductance, measurement_model = mlpe) fit # coefficients, log-likelihood, and AIC summary(fit) # adds standard errors, Wald tests, and the phi table # Conductance coefficients are on the LOG scale, so exponentiate for a # multiplicative reading: exp(theta) is the change in conductance per # one-unit (one standard deviation, here) increase in the covariate. coef(fit) exp(coef(fit)) logLik(fit) # carries a df attribute, so AIC()/BIC() work AIC(fit) # compare only fits using the same response and likelihood family head(fitted(fit)[, 1:4]) # fitted genetic distances head(residuals(fit)[, 1:4]) # observed minus fitted # A nested comparison: is forest cover worth its parameter? fit_alt <- terradish(melip.Fst ~ altitude, data = surface, conductance_model = loglinear_conductance, measurement_model = mlpe) anova(fit_alt, fit)
Changes nu without refitting the coefficients. Wishart objectives
and their derivatives are proportional to this user-supplied quantity.
terradish_rescale_nu(fit, nu)terradish_rescale_nu(fit, nu)
fit |
A full or slim fitted Wishart model. |
nu |
New finite, positive value of |
This is a sensitivity calculation, not an estimate of effective
information. It does not remedy misspecification or dependent histories.
If nu is divided by 20, standard errors grow by sqrt(20);
the likelihood and likelihood-ratio statistics are divided by 20. AIC
changes because its likelihood term changes while its parameter penalty
stays fixed. Report conclusions over a scientifically plausible range.
A fitted object with rescaled likelihood, curvature, AIC, and
comparison metadata. Estimates are unchanged; nu_original records
the original value across repeated rescalings. Standard errors computed by
summary() and vcov() use the rescaled curvature.
terradish_inference, aic_table,
terradish_cv_folds
# A small model-based example, with fixed information supplied explicitly. r <- terra::rast(nrows = 8, ncols = 8, xmin = 0, xmax = 8, ymin = 0, ymax = 8) terra::values(r) <- sin(seq_len(64) / 5) + seq_len(64) / 64 names(r) <- "x" sites <- terra::xyFromCell(r, c(1, 8, 20, 37, 57, 64)) graph <- conductance_surface(r, sites) simulated <- simulate_covariance_response(c(x = 0.6), ~x, graph, nu = 1000, seed = 93) response <- simulated$covariance fit <- terradish(response ~ x, graph, measurement_model = wishart_covariance, nu = 1000) sensitivity <- terradish_rescale_nu(fit, nu = 50) rbind(original = coef(fit), sensitivity = coef(sensitivity)) rbind(original = sqrt(diag(vcov(fit))), sensitivity = sqrt(diag(vcov(sensitivity))))# A small model-based example, with fixed information supplied explicitly. r <- terra::rast(nrows = 8, ncols = 8, xmin = 0, xmax = 8, ymin = 0, ymax = 8) terra::values(r) <- sin(seq_len(64) / 5) + seq_len(64) / 64 names(r) <- "x" sites <- terra::xyFromCell(r, c(1, 8, 20, 37, 57, 64)) graph <- conductance_surface(r, sites) simulated <- simulate_covariance_response(c(x = 0.6), ~x, graph, nu = 1000, seed = 93) response <- simulated$covariance fit <- terradish(response ~ x, graph, measurement_model = wishart_covariance, nu = 1000) sensitivity <- terradish_rescale_nu(fit, nu = 50) rbind(original = coef(fit), sensitivity = coef(sensitivity)) rbind(original = sqrt(diag(vcov(fit))), sensitivity = sqrt(diag(vcov(sensitivity))))
Performs an outer optimization over raster scale parameters by repeatedly
rescaling raster covariates, rebuilding or updating the conductance surface,
and refitting terradish.
terradish_scale_optim( formula, covariates, coords, scales = NULL, lower = NULL, upper = NULL, scale_grid = NULL, search = c("coordinate", "grid"), objective = c("aic", "logLik"), grid_points = 5L, maxit = 10L, tol = 0.1, scale_fun = "terradish", scale_args = NULL, postprocess = scale_covariates, directions = 8L, theta = NULL, verbose = FALSE, ... )terradish_scale_optim( formula, covariates, coords, scales = NULL, lower = NULL, upper = NULL, scale_grid = NULL, search = c("coordinate", "grid"), objective = c("aic", "logLik"), grid_points = 5L, maxit = 10L, tol = 0.1, scale_fun = "terradish", scale_args = NULL, postprocess = scale_covariates, directions = 8L, theta = NULL, verbose = FALSE, ... )
formula |
A terradish model formula. |
covariates |
A |
coords |
Focal coordinates passed to |
scales |
Optional starting values for the scale parameters. Required for
|
lower, upper
|
Lower and upper bounds for each scale parameter, in
map units for both built-in smoothing methods. The default uses the same
normalized, truncated Gaussian as the joint scale model. A useful upper
bound is one sixth of the smaller raster dimension times its cell width.
Used to construct |
scale_grid |
Optional named list of candidate scale values for
|
search |
One of |
objective |
Objective used for the outer optimization:
|
grid_points |
Number of equally spaced candidate scales per raster when
|
maxit |
Maximum number of outer coordinate-search iterations. |
tol |
Convergence tolerance for coordinate search, measured as the maximum absolute change in the scale vector between iterations. |
scale_fun |
Smoothing method: |
scale_args |
Optional named list passed to |
postprocess |
Optional function applied to the full scaled raster stack
before constructing the conductance surface. Defaults to
|
directions |
Neighborhood definition passed to
|
theta |
Optional starting values passed to |
verbose |
Print outer-optimization progress? |
... |
Additional arguments passed to |
This helper treats scale optimization as an outer problem around the usual
terradish fit, rather than as an inner Newton/BFGS parameter.
That makes it compatible with raster-scaling approaches such as
multiScaleR::kernel_scale.raster(), while keeping the terradish core
optimizer unchanged.
The selected values describe the transformation performed by
scale_fun in that function's units. They are tuning parameters for the
raster representation, not direct estimates of dispersal distance,
home-range size, organismal movement, or a causal ecological-process scale.
Report the search range and objective, and account for this outer selection
when describing model-selection uncertainty.
Each searched scale counts toward the parameter total used for information
criteria. Prefer the joint Gaussian model when its kernel matches the
scientific question; this outer search is a secondary option for other
kernels or an explicit scale grid. The default terradish kernel matches
the joint model, whereas multiScaleR uses a different construction even
when both scales are expressed in map units.
Coordinate search updates one raster scale at a time using
optimize while holding the others fixed. Grid search
evaluates every combination in scale_grid. In both modes, failed
terradish fits receive an infinite outer objective and are retained in the
returned evaluation table.
An object of class "terradish_scale_optim", a list with:
par |
The selected scale for each raster layer, in the units
|
value |
The outer objective at |
fit |
The fitted |
surface |
The conductance surface built from the rasters smoothed at
|
evaluations |
A data frame with one row per candidate scale
combination: the scale for each layer, the |
search |
The search strategy used, |
objective |
The name of the objective minimized, |
iterations, convergence
|
Number of outer iterations, and |
data(melip) melip.altitude <- terra::unwrap(melip.altitude) melip.forestcover <- terra::unwrap(melip.forestcover) melip.coords <- terra::unwrap(melip.coords) covariates <- c(melip.altitude, melip.forestcover) names(covariates) <- c("altitude", "forestcover") # Every candidate scale re-smooths the rasters and refits, so coarsen the # grid for the example. Use the full resolution in a real analysis. covariates <- terra::aggregate(covariates, fact = 3, na.rm = TRUE) # Express the bounds in map units, using the coarsened cell width. cell_width <- mean(terra::res(covariates)) # A 2-point grid over two layers is 4 # candidate fits; use a wider grid in practice. fit_scale <- terradish_scale_optim( melip.Fst ~ altitude + forestcover, covariates = covariates, coords = melip.coords, lower = cell_width, upper = 6 * cell_width, search = "grid", grid_points = 2, measurement_model = leastsquares, optimizer = "bfgs" ) fit_scale$par # selected smoothing scale per layer, in map units fit_scale$value # objective value there (lower is better) fit_scale$objective # which objective was minimized ("aic" by default) fit_scale$evaluations # every candidate tried, with its AIC and loglik coef(fit_scale$fit) # conductance coefficients at the selected scaledata(melip) melip.altitude <- terra::unwrap(melip.altitude) melip.forestcover <- terra::unwrap(melip.forestcover) melip.coords <- terra::unwrap(melip.coords) covariates <- c(melip.altitude, melip.forestcover) names(covariates) <- c("altitude", "forestcover") # Every candidate scale re-smooths the rasters and refits, so coarsen the # grid for the example. Use the full resolution in a real analysis. covariates <- terra::aggregate(covariates, fact = 3, na.rm = TRUE) # Express the bounds in map units, using the coarsened cell width. cell_width <- mean(terra::res(covariates)) # A 2-point grid over two layers is 4 # candidate fits; use a wider grid in practice. fit_scale <- terradish_scale_optim( melip.Fst ~ altitude + forestcover, covariates = covariates, coords = melip.coords, lower = cell_width, upper = 6 * cell_width, search = "grid", grid_points = 2, measurement_model = leastsquares, optimizer = "bfgs" ) fit_scale$par # selected smoothing scale per layer, in map units fit_scale$value # objective value there (lower is better) fit_scale$objective # which objective was minimized ("aic" by default) fit_scale$evaluations # every candidate tried, with its AIC and loglik coef(fit_scale$fit) # conductance coefficients at the selected scale
Times the linear-system setup and solve used internally by
terradish_algorithm for a fixed graph and conductance vector.
This is intended as a lightweight hardware-specific diagnostic for choosing
direct factorization settings on large rasters.
terradish_solver_benchmark( data, conductance = NULL, factorization = c("simplicial_ldl", "simplicial_ll", "supernodal_ll"), solve_backend = "matrix", n_replicates = 1L, perm = TRUE )terradish_solver_benchmark( data, conductance = NULL, factorization = c("simplicial_ldl", "simplicial_ll", "supernodal_ll"), solve_backend = "matrix", n_replicates = 1L, perm = TRUE )
data |
A |
conductance |
Optional numeric conductance vector with one value per graph vertex. Defaults to constant conductance. |
factorization |
Direct solver factorization modes to compare. Supported
values are |
solve_backend |
Direct-solver RHS solve backend to compare.
|
n_replicates |
Number of times to repeat each setting. |
perm |
Should CHOLMOD use a fill-reducing permutation? |
The benchmark isolates the sparse linear algebra step. It does not run a
full likelihood optimization, line search, measurement-model subproblem, or
Gaussian smoothing update. Use it to learn whether "simplicial_ldl",
"simplicial_ll", or "supernodal_ll" is fastest on a particular
machine and graph size, then pass the preferred choice through
solver_control = list(factorization = "...") in terradish
when using the direct solver. Use solve_backend to evaluate whether
either experimental C++ CHOLMOD path is faster for the right-hand-side count
used by the graph. The cached backend is most informative when compared
inside a full optimization, where nearby evaluations can reuse the same
symbolic analysis.
Very small rasters can give noisy timings because setup and R overhead are a large fraction of elapsed time. The comparison is most informative on graphs large enough that Cholesky factorization is a visible part of the fit time.
A data frame with one row per benchmark replicate and columns for graph size, factorization mode, solve backend, setup time, solve time, and total time.
library(terra) data(melip) melip.altitude <- terra::unwrap(melip.altitude) melip.coords <- terra::unwrap(melip.coords) surface <- conductance_surface(melip.altitude, melip.coords, directions = 8) terradish_solver_benchmark( surface, factorization = c("simplicial_ldl", "simplicial_ll"), n_replicates = 1 )library(terra) data(melip) melip.altitude <- terra::unwrap(melip.altitude) melip.coords <- terra::unwrap(melip.coords) surface <- conductance_surface(melip.altitude, melip.coords, directions = 8) terradish_solver_benchmark( surface, factorization = c("simplicial_ldl", "simplicial_ll"), n_replicates = 1 )
A function of class "terradish_measurement_model" that evaluates a
Wishart likelihood for an observed genetic covariance matrix.
This is the natural model when raw genotype data are summarized with
cov_from_genetic_data or cov_from_biallelic
rather than as pairwise distances. For an admissible squared-distance
representation coherently related to a centered positive semidefinite
covariance matrix, use generalized_wishart. For other pairwise
distances, use mlpe or leastsquares.
wishart_covariance( E, S, phi, nu, gradient = TRUE, hessian = TRUE, partial = TRUE, nonnegative = TRUE, validate = FALSE )wishart_covariance( E, S, phi, nu, gradient = TRUE, hessian = TRUE, partial = TRUE, nonnegative = TRUE, validate = FALSE )
E |
Conductance-implied covariance matrix: the generalized inverse of the graph Laplacian at the current conductance parameters. Passed automatically by the optimizer; users normally do not call this function directly. |
S |
Square, symmetric matrix of observed population-level genetic
covariance. Typically the output of |
phi |
Named numeric vector of nuisance parameters |
nu |
One finite positive number describing effective Wishart information.
You must supply it; the fit does not estimate it. It is not the marker
count. See the effective-information section and report sensitivity across
plausible values with |
gradient |
Logical. Compute gradient of the negative log-likelihood
with respect to |
hessian |
Logical. Compute Hessian with respect to |
partial |
Logical. Compute second partial derivatives with respect to
|
nonnegative |
Unused; present for interface consistency. |
validate |
Logical. Numerically validate gradients and Hessians via numDeriv? Very slow; for debugging small examples only. |
The nuisance parameters are:
tauNonnegative scale applied to the conductance-implied
covariance E. A value near zero signals no detectable graph-kernel
contribution under this response and model.
sigmaLog-scale nugget: the identity component added to the
model covariance is . It captures diagonal variation
not represented by the graph covariance, including sampling variation
and model mismatch. It is not a smoothing scale or a direct estimate of
genetic drift or population size.
The fitted covariance is . The
likelihood is evaluated on orthonormal site contrasts. Let have
orthonormal columns perpendicular to the constant vector, and let
and . The
negative log-likelihood, omitting terms constant in the fitted parameters
for a fixed response and supplied , is:
The response is a sample covariance on site contrasts with effective
degrees of freedom . Adding a constant to every entry of S
or centering it across sites does not change this objective. This is the
same objective as generalized_wishart(dist_from_cov(S)); the nugget
contributes to the expected squared distances.
Working on contrasts discards only the overall site-mean direction, which
carries no information in a centered genetic covariance.
Preparing the response. Use a coherent covariance construction,
such as diagonal = "gower" for grouped data in landgraph. A
"within" diagonal is on a different scale and is unsuitable for
Wishart fitting, even when the matrix happens to be positive definite.
Per-locus standardization up-weights rare variants; filter by minor-allele
frequency and assess sensitivity. Unequal group sizes create unequal
sampling variance that a common diagonal nugget does not automatically fix.
During fitting, terradish checks very small nugget estimates
for false convergence on the log scale. Improving finite-variance starts
retain the supplied bounds. An unrestricted nugget can instead be represented
at a verified numerical zero limit: its finite log value gives a positive
variance below floating-point covariance precision, and the one-sided
variance gradient must satisfy the stopping tolerance. This does not change
the likelihood or override fixed parameters. The fitted object records
fit$residual_variance_zero_limit; summary() omits Wald
uncertainty for sigma at that limit.
When phi is missing, a list with elements phi
(least-squares starting values for (tau, sigma)), lower
(c(0, -Inf)), and upper. Otherwise a list containing:
objective |
Negative log-likelihood. |
fitted |
Fitted covariance matrix |
boundary |
Logical; |
gradient |
Gradient with respect to |
hessian |
Hessian matrix with respect to |
gradient_E |
Gradient with respect to |
partial_E |
Jacobian of |
partial_S |
Jacobian of |
jacobian_E |
Function for reverse-mode AD through |
jacobian_S |
Function for reverse-mode AD through |
vignette("wishart-covariance", package = "terradish"),
vignette("individual-salamanders", package = "terradish").
For Wishart likelihoods, nu scales the log likelihood. Point
estimates do not depend on it, but standard errors, likelihood-ratio tests,
and information criteria do. It is a dispersion parameter that you set,
rather than the number of markers. Linkage and the population history
shared by loci can make effective information much smaller than the SNP
count. In forward-time simulations used to validate terradish, standard
errors at the SNP count were three to five times smaller than the spread
among replicate histories. Those model-based errors understated uncertainty.
Use terradish_cv_folds to compare conductance formulas, and
terradish_rescale_nu to report inference across plausible
values. For the same successful folds and nuisance settings, changing
nu rescales Wishart CV scores without changing their ranking.
Neither procedure identifies the true effective information or repairs
an unsuitable response matrix.
cov_from_genetic_data, cov_from_biallelic,
generalized_wishart, wishart_covariates,
simulate_covariance_response, terradish
E <- matrix(c(1.2, 0.3, 0.2, 0.3, 1.5, 0.4, 0.2, 0.4, 1.1), nrow = 3, byrow = TRUE) Sigma <- 0.8 * E + 0.2 * diag(3) S <- rWishart(1, df = 20, Sigma = Sigma)[,,1] / 20 wishart_covariance(E, S, phi = c(0.8, log(0.2)), nu = 20)E <- matrix(c(1.2, 0.3, 0.2, 0.3, 1.5, 0.4, 0.2, 0.4, 1.1), nrow = 3, byrow = TRUE) Sigma <- 0.8 * E + 0.2 * diag(3) S <- rWishart(1, df = 20, Sigma = Sigma)[,,1] / 20 wishart_covariance(E, S, phi = c(0.8, log(0.2)), nu = 20)
Creates a Wishart measurement model whose fitted covariance includes the
resistance-implied covariance plus one or more positive semidefinite kernels
built from site-level environmental covariates. Use this as the Wishart
analogue of mlpe_covariates when your measurement model is
generalized_wishart (squared-distance responses) or
wishart_covariance (covariance-matrix responses).
wishart_covariates( x, coords = NULL, transform = c("absdiff", "sqdiff", "euclidean", "manhattan"), scale = FALSE, model = c("wishart_covariance", "generalized_wishart") )wishart_covariates( x, coords = NULL, transform = c("absdiff", "sqdiff", "euclidean", "manhattan"), scale = FALSE, model = c("wishart_covariance", "generalized_wishart") )
x |
Site-level covariates. Supported inputs are the same as
|
coords |
Required when |
transform |
Pairwise environmental difference: |
scale |
Logical. Standardize site-level covariates to zero mean and unit variance before constructing kernels? Recommended when covariates use different units or scales. |
model |
Which Wishart likelihood to use.
|
A zero kernel coefficient sets the boundary flag while retaining conductance inference when the resistance coefficient is positive. Summaries mark zero kernel coefficients and give one-sided upper limits instead of symmetric intervals. Tests adding only kernel coefficients use a chi-bar-square reference with binomial weights. This reference is exact for information-orthogonal kernels and approximate otherwise. MLPE environmental coefficients are unconstrained and retain ordinary chi-square tests.
wishart_covariates() is the Wishart analogue of
mlpe_covariates, but environmental covariates enter the model
through positive semidefinite kernels. For each pairwise difference matrix
, the kernel is , where centers
sites. A kernel with a negative eigenvalue beyond numerical tolerance is
rejected. The "sqdiff" transform gives the centered outer product
of the original covariate. The fitted covariance is:
where is the resistance-implied covariance (generalized
inverse of the graph Laplacian at conductance parameters ),
is the weight on conductance-implied covariance,
are nonnegative kernel weights, and
is diagonal residual variance not represented by the
graph or environmental kernels.
The nuisance parameters estimated alongside are:
tauNonnegative scale on the resistance-implied covariance
. A value near zero indicates no detectable contribution from
conductance-implied covariance under this model.
lambda_<covariate>Nonnegative weight per unit of the corresponding pairwise environmental difference. A negative association is represented at the zero bound; MLPE environmental slopes are unconstrained. The weight is not a causal IBE effect.
sigmaLog-scale nugget: identity component added as
, representing diagonal variance not captured by the
other covariance components. It is not a smoothing scale or direct drift
or population-size estimate.
This design naturally preserves positive definiteness of when
the nugget is positive and all kernel weights are nonnegative. It is
evaluated on site contrasts, as in wishart_covariance. The
implied squared-distance mean is , using the same additive environmental terms as MLPE.
The likelihoods and coefficient constraints still differ. Use
wishart_covariates() when the response satisfies the selected Wishart
model's requirements and you can justify an effective degrees-of-freedom
value nu; use mlpe_covariates when nu is unknown
or when the distance-regression framework better matches the response.
Neither formulation cleanly separates causal IBE from IBR when predictors
and kernels are spatially correlated.
Scaling is applied to site values before the pairwise transform; kernels
are not normalized afterward. Site subsets retain these original pairwise
values and rebuild centered kernels without restandardizing the subset.
In the package's null-simulation audit, AIC selected an environmental kernel
in 19 of 40 runs at the SNP-count value of nu, and in none at a
replicate-calibrated value. These are results of that design, not general
error-rate guarantees. Use terradish_rescale_nu for sensitivity
and terradish_cv_folds(..., nuisance = "fixed") to assess predictive
support for a measurement-model extension.
A function of class "terradish_measurement_model" suitable
for the measurement_model argument of terradish and
terradish_grid. The nuisance parameter vector
estimated by terradish contains tau, one
lambda_<covariate> per environmental kernel, and sigma (in
that order). The function stores the kernel array in attribute
"kernel_covariates" and supports site-subsetting for
model fitting through terradish.
Fitting uses the same small-nugget safeguards as
wishart_covariance, retaining supplied bounds and fixed
parameters. A verified numerical zero residual-variance limit does not
remove the graph or environmental kernel coefficients. summary()
omits Wald uncertainty for the log-scale nugget at that limit.
See vignette("ibe-ibr-workflow", package = "terradish") for
environmental covariance kernels and their distinction from MLPE slopes.
Construct one pairwise_covariates object to use the same
environmental transforms and geographic distances in both likelihood
families. Their fitted conductance surfaces are conditional on these terms,
and conductance signs can change when the mean structure changes. Report
fits with and without pairwise effects. A uniform ~ 1 conductance
model with pairwise terms represents IBD plus IBE, not IBE alone.
A positive environmental coefficient means differentiation increases with
environmental difference conditional on resistance. It does not distinguish
assortative mating, dispersal filtering, local adaptation, or site variance
that rises at environmental extremes. MLPE coefficients are unconstrained;
Wishart kernel weights are nonnegative. Compare their effects in
resistance-distance units with terradish_ibe_ratio, which
divides by the resistance coefficient and propagates joint uncertainty.
Ratios are undefined when that denominator is zero.
For Wishart likelihoods, nu scales the log likelihood. Point
estimates do not depend on it, but standard errors, likelihood-ratio tests,
and information criteria do. It is a dispersion parameter that you set,
rather than the number of markers. Linkage and the population history
shared by loci can make effective information much smaller than the SNP
count. In forward-time simulations used to validate terradish, standard
errors at the SNP count were three to five times smaller than the spread
among replicate histories. Those model-based errors understated uncertainty.
Use terradish_cv_folds to compare conductance formulas, and
terradish_rescale_nu to report inference across plausible
values. For the same successful folds and nuisance settings, changing
nu rescales Wishart CV scores without changing their ranking.
Neither procedure identifies the true effective information or repairs
an unsuitable response matrix.
McCullagh P. 2009. Marginal likelihood for distance matrices. Statistica Sinica 19:631-649.
check_distance_response,
generalized_wishart, wishart_covariance,
mlpe_covariates,
pairwise_endpoint_covariates, terradish
library(terra) data(melip) melip.altitude <- terra::unwrap(melip.altitude) melip.forestcover <- terra::unwrap(melip.forestcover) melip.coords <- terra::unwrap(melip.coords) covariates <- c(melip.altitude, melip.forestcover) names(covariates) <- c("altitude", "forestcover") covariates <- scale_covariates(terra::aggregate(covariates, fact = 3, na.rm = TRUE)) surface <- conductance_surface(covariates, melip.coords, directions = 8) # Build an altitude kernel for a covariance-Wishart model. g_wc <- wishart_covariates(melip.altitude, coords = melip.coords, model = "wishart_covariance", scale = TRUE) inherits(g_wc, "terradish_measurement_model") # TRUE # Simulate an admissible covariance response, then fit the extended model. simulated <- simulate_covariance_response( theta = c(altitude = 0.2, forestcover = -0.1), formula = ~ altitude + forestcover, data = surface, tau = 0.8, sigma = 0.2, nu = 40, seed = 1 ) fit_joint_w <- terradish( simulated$covariance ~ altitude + forestcover, data = surface, conductance_model = loglinear_conductance, measurement_model = g_wc, nu = 40 ) # phi table shows tau, lambda_altitude, and sigma. summary(fit_joint_w)library(terra) data(melip) melip.altitude <- terra::unwrap(melip.altitude) melip.forestcover <- terra::unwrap(melip.forestcover) melip.coords <- terra::unwrap(melip.coords) covariates <- c(melip.altitude, melip.forestcover) names(covariates) <- c("altitude", "forestcover") covariates <- scale_covariates(terra::aggregate(covariates, fact = 3, na.rm = TRUE)) surface <- conductance_surface(covariates, melip.coords, directions = 8) # Build an altitude kernel for a covariance-Wishart model. g_wc <- wishart_covariates(melip.altitude, coords = melip.coords, model = "wishart_covariance", scale = TRUE) inherits(g_wc, "terradish_measurement_model") # TRUE # Simulate an admissible covariance response, then fit the extended model. simulated <- simulate_covariance_response( theta = c(altitude = 0.2, forestcover = -0.1), formula = ~ altitude + forestcover, data = surface, tau = 0.8, sigma = 0.2, nu = 40, seed = 1 ) fit_joint_w <- terradish( simulated$covariance ~ altitude + forestcover, data = surface, conductance_model = loglinear_conductance, measurement_model = g_wc, nu = 40 ) # phi table shows tau, lambda_altitude, and sigma. summary(fit_joint_w)