knitr::opts_chunk$set( collapse = TRUE, comment = "#>" )
library(gammacount)
In this package, the guiding principles for ensuring numerical accuracy of computations are
R
functions such as pgamma
.This vignette describes how these principles are applied to some specific computations in this package.
This package contains two helper functions (not exported), logsumexp
and logdiffexp
which can perform operations without leaving the log scale:
\begin{align}
\mathrm{logsumexp}(a, b) &= \log(e^a + e^b) \
\mathrm{logdiffexp}(a, b) &= \log(e^a - e^b)
\end{align}
Each works by factoring out the largest value ($\max(a, b)$ in the logsumexp
case, and $a$ in the logdiffexp
case).
\begin{align}
\mathrm{logsumexp}(a, b) &= \max(a,b) + \log(1 + e^{-|a-b|}) \
\mathrm{logdiffexp}(a, b) &= a + \log(1 - e^{b - a})
\end{align}
In this form, logsumexp
can take advantage of R
's log1p
function, and logdiffexp
uses either log1p(-exp(b-a))
or log(-expm1(b-a))
depending on the magnitude of $b-a$. Doing this minimizes the loss of accuracy relative to a naive implementation of this operation.
Recall that the density of the $n^{th}$ arrival time after a random start time has the density $$f_{\tau_n}(t) = \frac{\beta}{\alpha}(Q(n\alpha, \beta t) - Q((n-1)\alpha, \beta t)).$$ For $n \geq 2$, $\lim_{t\to 0} f_{\tau_n}(t) = \lim_{t\to \infty} f_{\tau_n}(t) = 0$. On the left tail this is because both $Q$ functions approach one, while on the right tail this is because both $Q$ functions approach zero.
On the log scale, finding the difference between two probabilities $p$ and $p+\epsilon$ is subject to less loss of accuracy when $p$ is small than large. So the density as written will be more accurate for large $t$ than for small $t$. To improve accuracy for small $t$, we notice that the density can also be written in terms of regularized lower incomplete gamma functions $$f_{\tau_n}(t) = \frac{\beta}{\alpha}(P((n-1)\alpha, \beta t) - P(n\alpha, \beta t)).$$ This form of the density has the opposite behavior for small $t$, as both $P$ functions approach zero.
We choose the distribution's mode as a convenient cutoff point to switch between using each representation and use the $P$ form for $t$ less than the mode, and use the $Q$ form for $t$ greater than the mode.
The CDF of the arrival time distribution is given by: $$\int_0^t f_{\tau_n}(u) \mathrm{d}u = (t Q(n\alpha, \alpha t) - n Q(n\alpha + 1, \alpha t)) - (t Q((n-1)\alpha, \alpha t) - (n-1)Q((n-1)\alpha + 1, \alpha t)) + 1$$ There is a lot of potential for loss of accuracy in the final $+1$, so we use equivalent forms for either the lower CDF $F_{\tau_n}(t)$ or the upper CDF $1 - F_{\tau_n}(t)$.
First, the upper CDF: \begin{align} 1 - F_{\tau_n}(t) &= 1 - \left[\left(\frac{\beta t}{\alpha} Q(n\alpha, \beta t) - n Q(n\alpha + 1, \beta t)\right) - \left(\frac{\beta t}{\alpha} Q((n-1)\alpha, \beta t) - (n-1)Q((n-1)\alpha + 1, \beta t)\right) + 1\right] \ &= \left(n Q(n\alpha + 1, \beta t) - \frac{\beta t}{\alpha} Q(n\alpha, \beta t)\right) - \left((n-1)Q((n-1)\alpha + 1, \beta t) - \frac{\beta t}{\alpha} Q((n-1)\alpha, \beta t)\right) \ &= \left(n Q(n\alpha + 1, \beta t) + \frac{\beta t}{\alpha} Q((n-1)\alpha, \beta t)\right) - \left((n-1)Q((n-1)\alpha + 1, \beta t) + \frac{\beta t}{\alpha} Q(n\alpha, \beta t)\right) \end{align}
This form, computed on the log scale, preserves accuracy in the right tail. The only call to logdiffexp
will be with arguments which are both large negative log probabilities.
For the lower CDF, we need to start with the alternate representation of the density, $$f_{\tau_n}(t) = \frac{\beta}{\alpha}(P((n-1)\alpha, \alpha t) - P(n\alpha, \alpha t))$$
Integrating this gives the CDF: \begin{align} F_{\tau_n}(t) &= \int_0^t \left[\frac{\beta}{\alpha}P((n-1)\alpha, \beta u) - \frac{\beta}{\alpha}P(n\alpha, \beta u)\right] \mathrm{d}u \ &= \left(\frac{\beta t}{\alpha} P((n-1)\alpha, \beta t) - (n-1)P((n-1)\alpha + 1, \beta t)\right) - \left(\frac{\beta t}{\alpha} P(n\alpha, \beta t) - n P(n\alpha + 1, \beta t)\right) \ &= \left(\frac{\beta t}{\alpha} P((n-1)\alpha, \beta t) + n P(n\alpha + 1, \beta t)\right) - \left(\frac{\beta t}{\alpha} P(n\alpha, \beta t) + (n-1)P((n-1)\alpha + 1, \beta t)\right) \end{align}
This form preserves accuracy on the left tail when computed on the log scale.
Both of these forms, however, lose accuracy in the opposite tail. There are three strategies for how to handle this:
lower.tail=F
and the lower CDF form otherwise. It's not my fault if the user requests a lower tail probability in the far right tail.logdiffexp(0, logp)
if the user requested the complementary probability. The median would be a natural choice for this point, but the median isn't available directly in this distribution. So we can use the mean or the mode instead.logdiffexp(0, logp)
. This can be used to passively compute using a median switchover point, but at the cost of some extra computation. The switchover point can also be made much higher, e.g. $\log(0.99)$, to only recompute if we are fairly certain there was loss of accuracy.Note: the median can be bounded with medians of gamma distributions, but qgamma
is several times as expensive as pgamma
. So if the median is desired as the switchover point, it's faster to use the passive option anyway.
In order to avoid double computation and opporunity for error, I chose the active tail choice option with the mean as a switchover point.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.