File: expint.Rnw

package info (click to toggle)
r-cran-expint 0.1-8-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 460 kB
  • sloc: ansic: 944; lisp: 65; makefile: 2
file content (480 lines) | stat: -rw-r--r-- 16,986 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
\documentclass[x11names,english]{article}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
  \usepackage{amsmath}
  \usepackage[round]{natbib}
  \usepackage{babel}
  \usepackage{microtype}
  \usepackage[scaled=0.92]{helvet}
  \usepackage[sc]{mathpazo}
  \usepackage[noae,inconsolata]{Sweave}
  \usepackage{framed}

  %\VignetteIndexEntry{expint user manual}
  %\VignettePackage{expint}
  %\SweaveUTF8

  \title{\pkg{expint}: Exponential integral and incomplete gamma function}
  \author{Vincent Goulet \\ Université Laval}
  \date{}

  %% Colors
  \usepackage{xcolor}
  \definecolor{link}{rgb}{0,0.4,0.6}             % internal links
  \definecolor{url}{rgb}{0.6,0,0}                % external links
  \definecolor{citation}{rgb}{0,0.5,0}           % citations
  \definecolor{codebg}{named}{LightYellow1}      % R code background

  %% Hyperlinks
  \usepackage{hyperref}
  \hypersetup{%
    pdfauthor={Vincent Goulet},
    colorlinks = {true},
    linktocpage = {true},
    urlcolor = {url},
    linkcolor = {link},
    citecolor = {citation},
    pdfpagemode = {UseOutlines},
    pdfstartview = {Fit},
    bookmarksopen = {true},
    bookmarksnumbered = {true},
    bookmarksdepth = {subsubsection}}

  %% Sweave Sinput and Soutput environments reinitialized to remove
  %% default configuration. Space between input and output blocks also
  %% reduced.
  \DefineVerbatimEnvironment{Sinput}{Verbatim}{}
  \DefineVerbatimEnvironment{Soutput}{Verbatim}{}
  \fvset{listparameters={\setlength{\topsep}{0pt}}}

  %% Environment Schunk redefined as an hybrid of environments
  %% snugshade* and leftbar of framed.sty.
  \makeatletter
  \renewenvironment{Schunk}{%
    \setlength{\topsep}{1pt}
    \def\FrameCommand##1{\hskip\@totalleftmargin
       \vrule width 2pt\colorbox{codebg}{\hspace{3pt}##1}%
      % There is no \@totalrightmargin, so:
      \hskip-\linewidth \hskip-\@totalleftmargin \hskip\columnwidth}%
    \MakeFramed {\advance\hsize-\width
      \@totalleftmargin\z@ \linewidth\hsize
      \advance\labelsep\fboxsep
      \@setminipage}%
  }{\par\unskip\@minipagefalse\endMakeFramed}
  \makeatother

  %% Additional commands similar to those of RJournal.sty.
  \newcommand{\pkg}[1]{\textbf{#1}}
  \newcommand{\code}[1]{\texttt{#1}}
  \newcommand{\samp}[1]{{`\normalfont\texttt{#1}'}}
  \newcommand{\file}[1]{{`\normalfont\textsf{#1}'}}

  \newcommand{\Ei}{\operatorname{Ei}}

  \bibliographystyle{plainnat}

<<echo=FALSE>>=
library(expint)
options(width = 60)
@

\begin{document}

\maketitle

\section{Introduction}
\label{sec:introduction}

The exponential integral
\begin{equation*}
  E_1(x) = \int_x^\infty \frac{e^{-t}}{t}\, dt,
  \quad x \in \mathbb{R}
\end{equation*}
and the incomplete gamma function
\begin{equation*}
  \Gamma(a, x) = \int_x^\infty t^{a-1} e^{-t}\, dt,
  \quad x > 0, \quad a \in \mathbb{R}
\end{equation*}
are two closely related functions that arise in various
fields of mathematics.

\pkg{expint} is a small package that intends to fill a gap in R's
support for mathematical functions by providing facilities to compute
the exponential integral and the incomplete gamma function.
Furthermore, and perhaps most conveniently for R package developers,
the package also gives easy access to the underlying C workhorses through
an API. The C routines are derived from the GNU Scientific Library
\citep[GSL;][]{GSL}.

Package \pkg{expint} started its life in version~2.0-0 of package
\pkg{actuar} \citep{actuar} where we extended the range
of admissible values in the computation of limited expected value
functions. This required an incomplete gamma function that accepts
negative values of argument $a$, as explained at the beginning of
Appendix~A of \citet{LossModels4e}.


\section{Exponential integral}
\label{sec:expint}

\citet[Section~5.1]{Abramowitz:1972} first define the exponential
integral as
\begin{equation}
  \label{eq:E1}
  E_1(x) = \int_x^\infty \frac{e^{-t}}{t}\, dt.
\end{equation}

An alternative definition (to be understood in terms of the Cauchy
principal value due to the singularity of the integrand at zero) is
\begin{equation*}
  \Ei(x) = - \int_{-x}^\infty \frac{e^{-t}}{t}\, dt
         = \int_{-\infty}^x \frac{e^t}{t}\, dt, \quad x > 0.
\end{equation*}
The above two definitions are related as follows:
\begin{equation}
  \label{eq:Ei_vs_E1}
  E_1(-x) = - \Ei(x), \quad x > 0.
\end{equation}

The exponential integral can also generalized to
\begin{equation*}
  E_n(x) = \int_1^\infty \frac{e^{-xt}}{t^n}\, dt, \quad
  n = 0, 1, 2, \dots, \quad x > 0,
\end{equation*}
where $n$ is then the \emph{order} of the integral. The latter
expression is closely related to the incomplete gamma function
(\autoref{sec:gammainc}) as follows:
\begin{equation}
  \label{eq:En_vs_gammainc}
  E_n(x) = x^{n - 1} \Gamma(1 - n, x).
\end{equation}
One should note that the first argument of function $\Gamma$ is
negative for $n > 1$.

The following recurrence relation holds between exponential integrals
of successive orders:
\begin{equation}
  \label{eq:En:recurrence}
  E_{n+1}(x) = \frac{1}{n} [e^{-x} - x E_n(x)].
\end{equation}
Finally, $E_n(x)$ has the following asymptotic expansion:
\begin{equation}
  \label{eq:En:asymptotic}
  E_n(x) \asymp \frac{e^{-x}}{x}
  \left(
    1 - \frac{n}{x} + \frac{n(n+1)}{x^2} - \frac{n(n+1)(n+2)}{x^3} +
    \dots
  \right).
\end{equation}


\section{Incomplete gamma function}
\label{sec:gammainc}

From a probability theory perspective, the incomplete gamma function
is usually defined as
\begin{equation*}
  P(a, x) = \frac{1}{\Gamma(a)} \int_0^x t^{a - 1} e^{-t}\, dt,
  \quad x > 0, \quad a > 0.
\end{equation*}
Function \code{pgamma} already implements this function in
R (just note the differing order of the arguments).

Now, the definition of the incomplete gamma function of interest for
this package is rather the following
\citep[Section~6.5]{Abramowitz:1972}:
\begin{equation}
  \label{eq:gammainc}
  \Gamma(a, x) = \int_x^\infty t^{a-1} e^{-t}\, dt,
  \quad x > 0, \quad a \in \mathbb{R}.
\end{equation}
Note that $a$ can be negative with this definition. Of course, for
$a > 0$ one has
\begin{equation}
  \label{eq:gammainc_vs_pgamma}
  \Gamma(a, x) = \Gamma(a) [1 - P(a, x)].
\end{equation}

Integration by parts of the integral in \eqref{eq:gammainc} yields the
relation
\begin{equation*}
  \Gamma(a, x) = -\frac{x^a e^{-x}}{a} + \frac{1}{a} \Gamma(a + 1, x).
\end{equation*}
When $a < 0$, this relation can be used repeatedly $k$ times until
$a + k$ is a positive number. The right hand side can then be
evaluated with \eqref{eq:gammainc_vs_pgamma}. If
$a = 0, -1, -2, \dots$, this calculation requires the value of
\begin{equation*}
  G(0, x) = \int_x^\infty \frac{e^{-t}}{t}\, dt = E_1(x),
\end{equation*}
the exponential integral defined in \eqref{eq:E1}.


\section{R interfaces}
\label{sec:interfaces}

Package \pkg{expint} provides one main and four auxiliary R functions
to compute the exponential integral, and one function to compute the
incomplete gamma function. Their signatures are the following:
\begin{Schunk}
\begin{Sinput}
expint(x, order = 1L, scale = FALSE)
expint_E1(x, scale = FALSE)
expint_E2(x, scale = FALSE)
expint_En(x, order, scale = FALSE)
expint_Ei(x, scale = FALSE)
gammainc(a, x)
\end{Sinput}
\end{Schunk}

Let us first go over function \code{gammainc} since there is less to
discuss. The function takes in argument two vectors or real numbers
(non-negative for argument \code{x}) and returns the value of
$\Gamma(a, x)$. The function is vectorized in arguments \code{a} and
\code{x}, so it works similar to, say, \code{pgamma}.

We now turn to the \code{expint} family of functions. Function
\code{expint} is a unified interface to compute exponential integrals
$E_n(x)$ of any (non-negative) order, with default the most common
case $E_1(x)$. The function is vectorized in arguments \code{x} and
\code{order}. In other words, one can compute the exponential integral
of a different order for each value of $x$.
<<echo=TRUE>>=
expint(c(1.275, 10, 12.3), order = 1:3)
@

Argument \code{order} should be a vector of integers. Non-integer
values are silently coerced to integers using truncation towards zero.

When argument \code{scale} is \code{TRUE}, the result is scaled by
$e^{x}$.

Functions \code{expint\_E1}, \code{expint\_E2} and \code{expint\_En} are
simpler, slightly faster ways to directly compute exponential
integrals $E_1(x)$, $E_2(x)$ and $E_n(x)$, the latter for a \emph{single}
order $n$ (the first value of \code{order} if \code{order} is a
vector).
<<echo=TRUE>>=
expint_E1(1.275)
expint_E2(10)
expint_En(12.3, order = 3L)
@

Finally, function \code{expint\_Ei} is provided as a convenience to
compute $\Ei(x)$ using \eqref{eq:Ei_vs_E1}.
<<echo=TRUE>>=
expint_Ei(5)
-expint_E1(-5)     # same
@


\section{Accessing the C routines}
\label{sec:api}

The actual workhorses behind the R functions of
\autoref{sec:interfaces} are C routines with the following prototypes:
\begin{Schunk}
\begin{Sinput}
double expint_E1(double x, int scale);
double expint_E2(double x, int scale);
double expint_En(double x, int order, int scale);
double gamma_inc(double a, double x);
\end{Sinput}
\end{Schunk}

Package \pkg{expint} makes these routines available to other packages
through declarations in the header file \file{include/expintAPI.h} in
the package installation directory. The developer of some package
\pkg{pkg} who wants to use a routine --- say \code{expint\_E1} --- in
her code should proceed as follows.
\begin{enumerate}
\item Add package \pkg{expint} to the \code{Imports} and \code{LinkingTo}
  directives of the \file{DESCRIPTION} file of \pkg{pkg};
\item Add an entry \samp{import(expint)} in the \file{NAMESPACE} file
  of \pkg{pkg};
\item Define the routine with a call to \code{R\_GetCCallable} in the
  initialization routine \code{R\_init\_pkg} of \pkg{pkg}
  \citep[Section~5.4]{WRE}. For the current example, the file
  \file{src/init.c} of \pkg{pkg} would contain the following code:
\begin{Schunk}
\begin{Sinput}
void R_init_pkg(DllInfo *dll)
{
    R_registerRoutines( /* native routine registration */ );

    pkg_expint_E1 = (double(*)(double,int,int))
                    R_GetCCallable("expint", "expint_E1");
}
\end{Sinput}
\end{Schunk}
\item Define a native routine interface that will call
  \code{expint\_E1}, say \code{pkg\_expint\_E1} to avoid any name
  clash, in \file{src/init.c} as follows:
\begin{Schunk}
\begin{Sinput}
double(*pkg_expint_E1)(double,int);
\end{Sinput}
\end{Schunk}
\item Declare the routine in a header file of \pkg{pkg} with the
  keyword \code{extern} to expose the interface to all routines of the
  package. In our example, file \file{src/pkg.h} would contain:
\begin{Schunk}
\begin{Sinput}
extern double(*pkg_expint_E1)(double,int);
\end{Sinput}
\end{Schunk}
\item Include the package header file \file{pkg.h} in any C file
  making use of routine \code{pkg\_expint\_E1}.
\end{enumerate}

To help developers get started, \pkg{expint} ships with a complete
test package implementing the above; see the \file{example\_API}
sub-directory in the installation directory. This test package uses
the \code{.External} R to C interface and, as a bonus, shows how to
vectorize an R function on the C side (the code for this being mostly
derived from base R).

There are various ways to define a package API. The approach described
above was derived from package \pkg{zoo} \citep{zoo}. Package
\pkg{xts} \citep{xts} --- and probably a few others on CRAN --- draws
from \pkg{Matrix} \citep{Matrix} to propose a somewhat simpler
approach where the API exposes routines that can be used directly in a
package. However, the provided header file can be included only once
in a package, otherwise one gets \samp{duplicate symbols} errors at
link time. This constraint does no show in the example provided with
\pkg{xts} or in packages \pkg{RcppXts} \citep{RcppXts} and \pkg{TTR}
\citep{TTR} that link to it (the only two at the time of writing). A
way around the issue is to define a native routine calling the
routines exposed in the API. In this scenario, tests we conducted
proved the approach we retained to be up to 10\% faster most of the
time.


\section{Implementation details}
\label{sec:implementation}

As already stated, the C routines mentioned in
\autoref{sec:api} are derived from code in the GNU Scientific Library
\citep{GSL}.

For exponential integrals, the main routine \code{expint\_E1} computes
$E_1(x)$ using Chebyshev expansions \citep[chapter~3]{Gil:2007}.
Routine \code{expint\_E2} computes $E_2(x)$ using \code{expint\_E1}
with relation \eqref{eq:En:recurrence} for $x < 100$, and using the
asymptotic expression \eqref{eq:En:asymptotic} otherwise. Routine
\code{expint\_En} simply relies on \code{gamma\_inc} to compute
$E_n(x)$ for $n > 2$ through relation \eqref{eq:En_vs_gammainc}.

For the sake of providing routines that better fit within the
R ecosystem and coding style, we made the following changes
to the original GSL code:
\begin{enumerate}
\item routines now compute a single value and return their result by
  value;
\item accordingly, calculation of the approximation error was dropped
  in all routines;
\item most importantly, \code{gamma\_inc} does not compute
  $\Gamma(a, x)$ for $a > 0$ with \eqref{eq:gammainc_vs_pgamma} using
  the GSL routines, but rather using routines \code{gammafn} and
  \code{pgamma} part of the R API.
\end{enumerate}
The following illustrates the last point.
<<echo=FALSE>>=
op <- options() # remember default number of digits
@
<<echo=TRUE>>=
options(digits = 20)
gammainc(1.2, 3)
gamma(1.2) * pgamma(3, 1.2, 1, lower = FALSE)
@
<<echo=FALSE>>=
options(op)     # restore defaults
@

\section{Alternative packages}
\label{sec:alternatives}

The Comprehensive R Archive Network\footnote{%
  \url{https://cran.r-project.org}} %
(CRAN) contains a number of packages with features overlapping
\pkg{expint}. We review the similarities and differences here.

The closest package in functionality is \pkg{gsl} \citep{gsl-package}.
This package is an R wrapper for the special functions and
quasi random number generators of the GNU Scientific Library. As such,
it provides access to basically the same C code as used in
\pkg{expint}. Apart from the changes to the GSL code mentioned in
\autoref{sec:implementation}, the main difference between the two
packages is that \pkg{gsl} requires that the GSL be installed on one's
system, whereas \pkg{expint} is a regular, free standing R
package.

Package \pkg{VGAM} \citep{VGAM} is a large, high quality package that
provides functions to compute the exponential integral $\Ei(x)$ for
real values, as well as $e^{-x} \Ei(x)$ and $E_1(x)$ and their
derivatives (up to the third derivative). Functions \code{expint},
\code{expexpint} and \code{expint.E1} are wrappers to the
Netlib\footnote{%
  \url{http://www.netlib.org}} %
FORTRAN subroutines in file \code{ei.f}. \pkg{VGAM} does
not provide an API to its C routines.

Package \pkg{pracma} \citep{pracma} provides a large number of
functions from numerical analysis, linear algebra, numerical
optimization, differential equations and special functions. Its
versions of \code{expint}, \code{expint\_E1}, \code{expint\_Ei} and
\code{gammainc} are entirely written in R with perhaps less
focus on numerical accuracy than the GSL and Netlib
implementations. The version of \code{gammainc} only supports positive
values of $a$.

Package \pkg{frmqa} \citep{frmqa} has a function
\code{gamma\_inc\_err} that computes the incomplete gamma function
using the incomplete Laplace integral, but it is only valid for
$a = j + \frac{1}{2}$, $j = 0, 1, 2, \dots$.

Package \pkg{zipfR} \citep{zipfR} introduces a set of functions to
compute various quantities related to the gamma and incomplete gamma
functions, but these are essentially wrappers around the base
R functions \code{gamma} and \code{pgamma} with no new
functionalities.


\section{Examples}
\label{sec:examples}

We tabulate the values of $E_n(x)$ for $x = 1.275, 10, 12.3$ and
$n = 1, 2, \dots, 10$ as found in examples~4--6 of
\citet[section~5.3]{Abramowitz:1972}.
<<echo=TRUE>>=
x <- c(1.275, 10, 12.3)
n <- 1:10
structure(t(outer(x, n, expint)),
          dimnames = list(n, paste("x =", x)))
@

We also tabulate the values of $\Gamma(a, x)$ for
$a = -1.5, -1, -0.5, 1$ and $x = 1, 2, \dots, 10$.
<<echo=TRUE>>=
a <- c(-1.5, -1, -0.5, 1)
x <- 1:10
structure(t(outer(a, x, gammainc)),
          dimnames = list(x, paste("a =", a)))
@


\section{Acknowledgments}

We built on the source code of R and many of the packages cited in
this paper to create \pkg{expint}, so the R Core Team and the package
developers deserve credit. We also extend our thanks to Dirk
Eddelbuettel who was of great help in putting together the package
API, through both his posts in online forums and private
communication. Joshua Ulrich provided a fix to the API infrastructure
to avoid duplicated symbols that was implemented in version 0.1-6 of
the package.


\bibliography{expint}

\end{document}