Skip to contents

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:

#include <integratecpp.h>

Second, you have to define the integrand as a Callable that is invocable by const double and returns double, for example a lambda-functor:

auto fn = [](const double x) {
    return x;
};

Third, you can use the integrate routine:

const auto result = integratecpp::integrate(fn, 0., 1.);

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:

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(...) or integratecpp::integrator::operator()(...) will create a std::vector<int> and a std::vector<double> of length limit and 4 * limit, respectively. Future versions might make workspace variables class members of the integratecpp::integrator or allow to provide them externally, see #9.
  • The current version of integratecpp is licensed under GPL (>=3) due to its dependence and linking to Rcpp, the header-only library is licensed under LGPL (>=3) as it only depends only on STL headers and <R_ext/Applic.h>. The latter is licensed under LGPL (>= 2.1) (see doc/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.