R/niche_presence_only.R

Defines functions niche_presence_only

Documented in niche_presence_only

#' Fit presence-only Gaussian niche model
#'
#' Fits a multivariate normal niche model using only presence records,
#' with no background correction (Jimenez et al. 2019, Eq. 2). Uses
#' the compiled C++ backend via an external pointer for fast evaluation.
#'
#' The minimised objective is the negative log-likelihood of the
#' unconstrained multivariate normal:
#' \deqn{
#'   -\log\mathcal{L} =
#'   \frac{n}{2}\log|\Sigma|
#'   + \frac{1}{2}\sum_{i=1}^{n} q_i
#' }
#' where \eqn{q_i = (\mathbf{x}_i - \mu)^\top \Sigma^{-1}
#' (\mathbf{x}_i - \mu)}.
#'
#' Supports both single-start (\code{start} is a numeric vector) and
#' multi-start (\code{start} is a list of numeric vectors) optimisation.
#'
#' @param occ Numeric matrix (\eqn{n \times p}) of environmental values
#'   at presence points.
#' @param eta Numeric scalar, shape parameter for the LKJ C-vine prior on
#'   the correlation matrix (default 1 = uniform over correlations).
#' @param start A numeric vector (single-start) or list of numeric vectors
#'   (multi-start). Use [start_theta()] or [start_theta_multiple()] to
#'   generate starting values.
#' @param ... Ignored. Present for wrapper compatibility.
#'
#' @return A list with components:
#' \describe{
#'   \item{\code{theta}}{Best parameter vector.}
#'   \item{\code{value}}{Negative log-likelihood at the optimum.}
#'   \item{\code{conv}}{Convergence code (0 = success).}
#'   \item{\code{all_results}}{Data frame of all starts (multi-start only).}
#' }
#'
#' @references
#' Jimenez, L., Soberon, J., Christen, J. A., & Soto, D. (2019).
#' On the problem of modeling a fundamental niche from occurrence data.
#' \emph{Ecological Modelling}, 397, 109823.
#'
#' @seealso [optimize_niche()] for the unified fitting interface,
#'   [loglik_niche_math_presence_only()] for the R-level log-likelihood.
#'
#' @examples
#' occ    <- as.matrix(example_env_occ_3d)
#' theta0 <- start_theta(example_env_occ_3d)
#' res    <- niche_presence_only(occ = occ, start = theta0)
#' res$value
#'
#' @export
niche_presence_only <- function(occ, eta = 1, start = NULL, ...) {
  if (is.numeric(start) && !all(is.finite(start))) {
    stop("`start` must contain only finite values (no NA, NaN, Inf).")
  }

  # Build compiled C++ presence-only objective
  xptr <- create_niche_obj_ptr(
    env_occ    = as.matrix(occ),
    env_m      = NULL,
    likelihood = "presence_only",
    eta        = eta
  )

  # --- SINGLE START ---
  if (is.numeric(start)) {
    res <- optimize_niche_xptr(
      start       = start,
      xptr        = xptr,
      multi_start = FALSE
    )
    res$theta <- res$par
    res$par <- NULL
    return(res)
  }

  # --- MULTI-START ---
  if (is.list(start)) {
    res <- optimize_niche_xptr(
      start       = start,
      xptr        = xptr,
      multi_start = TRUE
    )
    res$theta <- res$par
    res$par <- NULL
    return(res)
  }

  stop("Start must be a numeric vector (single-start) or a list of numeric vectors (multi-start).")
}

Try the xnicher package in your browser

Any scripts or data that you put into this service are public.

xnicher documentation built on Sept. 15, 2026, 5:08 p.m.