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);
5); // 5 is not a valid index value here y(
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
1.); Type f = sqrt(-
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>
operator() ()
Type objective_function<Type>::
{// Extra line needed
feenableexcept(FE_INVALID | FE_OVERFLOW | FE_DIVBYZERO | FE_UNDERFLOW);
DATA_SCALAR(lambda);
PARAMETER(x);
Type f;1.); // FE_INVALID ( sqrt(-1.) returns NaN )
f = sqrt(-//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: