The package integratecpp
provides a header-only C++11 interface to R’s C-API for numerical integration.
Installation
You can install the development version of integratecpp
like so:
# R
remotes::install_github("hsloot/integratecpp")
To include the header into your C++ source files for building with Rcpp, use
// C++
// [[Rcpp::plugins(cpp11)]]
// [[Rcpp::depends(integratecpp)]]
#include <integratecpp.h>
// your code
To use the header in source files of an R-package, include the following lines in your DESCRIPTION
file:
LinkingTo: integratecpp
Note that the header includes only C++
standard library headers and <R_ext/Applic.h>
.
How to use it?
Suppose you want to integrate the identity function over the unit interval. First, you include the required headers (and Rcpp attributes, if Rcpp is used), for example:
Second, you have to define the integrand as a Callable
that is invocable by const double
and returns double
, for example a lambda-functor:
Third, you can use the integrate routine:
Better, the last part should be enclosed in a try-catch block:
try {
const auto result = integratecpp::integrate(fn, 0., 1.);
// ...
} catch (const integratecpp::integration_logic_error &e) {
// ...
} catch (const integratecpp::integration_runtime_error &e) {
// ...
}
Optional configurations similar to those of stats::integrate
are also available if needed. For a more realistic example, see “Using integratecpp
”.
Why is this useful?
Many R package authors implement critical parts, including numeric integration, in C, Fortran or C++ to improve performance. However, while R provides an API for C and it is possible to mix C and C++, using the C-API in C++ code can pose a higher burden for those more familiar with R and Rcpp than C++ or C.
Using the C-API for the numerical integration routines requires adhering to the interface of the functions Rdqags
or Rdqagi
. This can be done by creating a callback functor taking a void *
pointer to the original function, which is then internally cast to the correct type and is used to overwrite an array of doubles with corresponding function evaluations. This is rather complicated and requires being more familiarity with pointers. Additionally, it requires translating error codes into a proper error message. To make it worse, not guarding callback functions against C++ exceptions introduces possible undefined behavior.
This packages bridges this gap by providing a simple, easy-to-use C++11 wrapper for R’s C-API for numerical integration.
Alternatives
There are alternatives to using integratecpp
or R’s C-API for numerical integration in compiled code of R packages. Two examples are:
- Linking to the GNU scientific library, possibly using RcppGSL.
- Using RcppNumerical, which provides a similar approach to ours.
Both approaches provide a finer control over the specific integration algorithms than R does. The following table provides a summary.
Approach | Depends | Imports | LinkingTo | SystemRequirements | External dependency | Additional features |
---|---|---|---|---|---|---|
integratecpp |
R >= 3.1 |
(Rcpp1) | (Rcpp) | C++11 | ❌ | |
C-API | ❌ | |||||
gsl | (Rcpp) | (Rcpp, RcppGSL) | gsl | ✅ | ||
RcppNumerical | Rcpp | Rcpp, RcppEigen | ✅ |
What separates our approach are little additional dependencies (zero, if vendored) and an intuitive pure-C++ API which does not rely on Rcpp itself. Hence, as Rdqags
and Rdqagi
are not using longjumps themselves, our approach can be used in a pure C++ back-end.2 A comparison of different numerical integration approaches in C++ is summarized in the article “Comparing numerical integration packages”.
Outlook
The current version of integratecpp
has the following shortcomings which could be addressed in future versions:
- We currently import and link to Rcpp to generate test functions which are not exported. Future versions might remove this dependency, see #8.
- R’s C-API for numerical integration allows reusing workspace variables. We have not implemented this feature, i.e., each call to
integratecpp::integrate(...)
orintegratecpp::integrator::operator()(...)
will create astd::vector<int>
and astd::vector<double>
of lengthlimit
and4 * limit
, respectively. Future versions might make workspace variables class members of theintegratecpp::integrator
or allow to provide them externally, see #9. - The current version of
integratecpp
is licensed underGPL (>=3)
due to its dependence and linking to Rcpp, the header-only library is licensed underLGPL (>=3)
as it only depends only on STL headers and<R_ext/Applic.h>
. The latter is licensed underLGPL (>= 2.1)
(seedoc/COPYRIGHTS
). Hence, future versions might relicense the package under a more permissive library if the Rcpp dependency can be removed, see #10.
Code of Conduct
Please note that the integratecpp
project is released with a Contributor Code of Conduct. By contributing to this project, you agree to abide by its terms.