8 Compilation and run time errors

The R interface to the debugger (gdb) is documented as part of the R help system, i.e. you can type ?gdbsource in R to get info. The current document only adresses isses that the relate to C++.

8.1 Compilation errors

It may be hard to understand the compilation errors for the following reasons

  • The Eigen libraries use templated C++ which generate non-intuitive error messages.

8.2 Run time errors

Run time errors are broadly speaking of two types:

  • Out-of-bounds (you are “walking out of an array”)
  • Floating point exceptions

You can use the debugger to locate both types of errors, but the procedure is a little bit different in the two cases. The following assumes that you have the GNU debugger gdb installed.

8.2.1 Out-of-bounds error

An example is:

vector<Type> y(4);
y(5);                // 5 is not a valid index value here

This will cause TMB and R to crash with the following error message:

TMB has received an error from Eigen. The following condition was not met: index >= 0 && index < size() Please check your matrix-vector bounds etc., or run your program through a debugger. Aborted (core dumped)

So, you must restart R and give the commands

library(TMB)
gdbsource("my_project.R")

#5 objective_function::operator() (this=) at nan_error_ex.cpp:11

and you can see that the debugger points to line number 11 in the .cpp file. gdbsource() is an R function that is part of TMB.

8.2.2 Floating point exception

If you on the other hand perform an illegal mathematical operation, such as

Type f = sqrt(-1.);

R will not crash, but the objective function will return a NaN value. However, you will not know in which part of your C++ code the error occured. By including the fenv.h library (part of many C++ compilers, but can otherwise be downloaded from http://www.scs.stanford.edu/histar/src/uinc/fenv.h)

nan_error_ex.cpp:

// Illustrates how to make the debugger catch a floating point error.
#include <TMB.hpp>
#include <fenv.h> // Extra line needed

template<class Type>
Type objective_function<Type>::operator() ()
{
  feenableexcept(FE_INVALID | FE_OVERFLOW | FE_DIVBYZERO | FE_UNDERFLOW); // Extra line needed
    
  DATA_SCALAR(lambda);
  PARAMETER(x);
  Type f;
  f = sqrt(-1.);        // FE_INVALID   ( sqrt(-1.) returns NaN )
  //f = 1./0.;          // FE_DIVBYZERO ( division by zero )
  //f = exp(100000.);   // FE_OVERFLOW  ( exp(100000.) returns Inf )   [Does not work on all platforms]
  //f = exp(-100000.);  // FE_UNDERFLOW ( exp(-100000.) returns 0 )
  return f;
}

a floating point exception will be turned into an actual error that can be picked up by the debugger. There are only two extra lines that need to be included (“//Extra line needed” in the above example).

When we try to run this program in the usual way, the program crashes:

source("nan_error_ex.R")

Floating point exception (core dumped) tmp3>

At this stage you should run the debugger to find out that the floating point exception occurs at line number 14:

library(TMB)
gdbsource("nan_error_ex.R")

#1 0x00007ffff0e7eb09 in objective_function::operator() (this=) at nan_error_ex.cpp:14

This enabling of floating point errors applies to R as well as the TMB program. For more elaborate R-scripts it may therefore happen that a NaN occurs in the R-script before the floating point exception in the TMB program (i.e. the problem of interest) happens. To circumvent this problem one can run without NaN debugging enabled and save the parameter vector that gave the floating point exception (e.g. badpar <- obj$env$last.par after the NaN evaluation), then enable NaN debugging, re-compile, and evaluate obj$env$f( badpar, type="double").

8.2.3 Missing casts for vectorized functions

TMB vectorized functions cannot be called directly with expressions, for example the following will fail to compile:

DATA_VECTOR(x);
// Don't do this! Doesn't compile
vector<Type> out = lgamma(x + 1);

error: could not convert ‘atomic::D_lgamma(const CppAD::vector&) … from ‘double’ to ‘Eigen::CwiseBinaryOp<Eigen::internal::scalar_sum_op<double, double>, … >’

Eigen lazy-evaluates expressions, and the templating of lgamma means we expect to return a “x + y”-typed object, which it obviously can’t do.

To work around this, cast the input:

DATA_VECTOR(x);
vector<Type> out = lgamma(vector<Type>(x + 1));