Skip to content

Commit

Permalink
try adding gengamma
Browse files Browse the repository at this point in the history
  • Loading branch information
James-Thorson committed Jan 5, 2024
1 parent 8ceb76a commit e6a2c25
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 1 deletion.
17 changes: 17 additions & 0 deletions R/families.R
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,23 @@ lognormal <- function(link = "log") {
add_to_family(x)
}

#' @export
#' @rdname families
#' @examples
#' gengamma(link = "log")
gengamma <- function(link = "log") {
linktemp <- substitute(link)
if (!is.character(linktemp))
linktemp <- deparse(linktemp)
okLinks <- c("identity", "log", "inverse")
if (linktemp %in% okLinks)
stats <- stats::make.link(linktemp)
else if (is.character(link))
stats <- stats::make.link(link)
x <- c(list(family = "gengamma", link = linktemp), stats)
add_to_family(x)
}

#' @details The families ending in `_mix()` are 2-component mixtures where each
#' distribution has its own mean but a shared scale parameter.
#' (Thorson et al. 2011). See the model-description vignette for details.
Expand Down
2 changes: 2 additions & 0 deletions R/fit.R
Original file line number Diff line number Diff line change
Expand Up @@ -1137,6 +1137,7 @@ sdmTMB <- function(
ln_kappa = matrix(0, 2L, n_m),
# ln_kappa = rep(log(sqrt(8) / median(stats::dist(spde$mesh$loc))), 2),
thetaf = 0,
gengamma_Q = 1, # Not defined at exactly 0
logit_p_mix = 0,
log_ratio_mix = 0,
ln_phi = rep(0, n_m),
Expand Down Expand Up @@ -1168,6 +1169,7 @@ sdmTMB <- function(
tmb_map$b_j <- NULL
if (delta) tmb_map$b_j2 <- NULL
if (family$family[[1]] == "tweedie") tmb_map$thetaf <- NULL
if (family$family[[1]] == "gengamma") tmb_map$gengamma_Q <- NULL
if (family$family[[1]] %in% c("gamma_mix", "lognormal_mix", "nbinom2_mix")) {
tmb_map$log_ratio_mix <- NULL
tmb_map$logit_p_mix <- NULL
Expand Down
9 changes: 8 additions & 1 deletion src/sdmTMB.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ enum valid_family {
censored_poisson_family = 12,
gamma_mix_family = 13,
lognormal_mix_family = 14,
nbinom2_mix_family = 15
nbinom2_mix_family = 15,
gengamma_family = 16
};

enum valid_link {
Expand Down Expand Up @@ -232,6 +233,7 @@ Type objective_function<Type>::operator()()
PARAMETER_ARRAY(ln_kappa); // Matern parameter

PARAMETER(thetaf); // tweedie only
PARAMETER(gengamma_Q); // gengamma only
PARAMETER(logit_p_mix); // ECE / positive mixture only
PARAMETER(log_ratio_mix); // ECE / positive mixture only

Expand Down Expand Up @@ -933,6 +935,11 @@ Type objective_function<Type>::operator()()
}
break;
}
case gengamma_family: {
tmp_ll = sdmTMB::dgengamma(y_i(i,m), mu_i(i,m), phi(m), gengamma_Q, true);
SIMULATE{y_i(i,m) = sdmTMB::rgengamma(mu_i(i,m), phi(m), gengamma_Q);}
break;
}
default:
error("Family not implemented.");
}
Expand Down
55 changes: 55 additions & 0 deletions src/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,61 @@ bool isNA(Type x) {
return R_IsNA(asDouble(x));
}

// dgengamma
// Written by J. Thorson based on scripts listed below
// using Prentice-1974 parameterization for lambda instead of k, so that lognormal occurs as lambda -> 0
// using mean parameterization to back out theta
// CV is a function of sigma and lambda and NOT mean (i.e., CV is fixed for all values of mean)
// See: C:\Users\James.Thorson\Desktop\Work files\AFSC\2021-10 -- Generalized gamma-lognormal\Explore gengamma.R
template<class Type>
Type dgengamma( Type x,
Type mean,
Type sigma,
Type Q,
int give_log=0){


// First convert from mean to mu
// Rename based on flexdist
Type lambda = Q;
// Using https://stats.stackexchange.com/questions/345564/generalized-gamma-log-normal-as-limiting-special-case
Type k = pow( lambda, -2);
Type beta = pow( sigma, -1) * lambda;
// Use wikipedia expression for the mean: mean = a * gamma((d+1)/p) / gamma(d/p) where d/p = k, theta = a, and beta = p
// https://en.wikipedia.org/wiki/Generalized_gamma_distribution#Software_implementation
Type log_theta = log(mean) - lgamma( (k*beta+1)/beta ) + lgamma( k );
// Using https://stats.stackexchange.com/questions/345564/generalized-gamma-log-normal-as-limiting-special-case
Type mu = log_theta + log(k) / beta;

// Next evaluate PDF
// from https://github.com/chjackson/flexsurv-dev/blob/master/src/gengamma.h#L54-L56
Type y = log(x);
Type w = (y - mu) / sigma;
Type qi = 1/square(Q);
Type qw = Q * w;
Type logres = -log(sigma*x) + 0.5*log(square(lambda)) * (1 - 2 * qi) + qi * (qw - exp(qw)) - lgamma(qi);

// return stuff
if(give_log) return logres; else return exp(logres);
}

// rgengamma
// Written by J. Thorson based on scripts listed below
template<class Type>
Type rgengamma( Type mean,
Type sigma,
Type Q){

// See: C:\Users\James.Thorson\Desktop\Work files\AFSC\2021-10 -- Generalized gamma-lognormal\Explore gengamma.R
Type lambda = Q;
Type k = pow( lambda, -2 );
Type beta = pow( sigma, -1 ) * lambda;
Type log_theta = log(mean) - lgamma( (k*beta+1)/beta ) + lgamma( k );
Type w = log(rgamma(k, Type(1.0)));
Type y = w/beta + log_theta;
return exp(y);
}

template <class Type>
Type ppois_log(Type x, Type lambda) {
return atomic::Rmath::Rf_ppois(asDouble(x), asDouble(lambda), true, true);
Expand Down

0 comments on commit e6a2c25

Please sign in to comment.