RTMB tips

requirements for RTMB functions

  • the objective function must be differentiable with respect to parameters (no if(), abs(), round(), min(), max() depending on parameters)
  • exotic probability distributions are available in the RTMBdist package (on CRAN)
  • aliases to base functions made before call to library(RTMB) might not work
  • use of <-[ (see here) etc.
    • specifically, if you use the c() function, or if you use the diag<- function (which sets the diagonal of a matrix) or the [<- function (which assigns values within a matrix), you need to add e.g. ADoverload("[<-") to the beginning of your function
  • You can only “grow” empty vectors if initialized with numeric(0), not c() or NULL (i.e. x <- c(); x[1] <- 2 throws an error, but x <- numeric(0); x[1] <- 2 does work), see here. In any case it is better practice to preallocate instead of growing vectors, e.g. x <- numeric(1); x[1] <- 2 (see chapter 2 of the R Inferno).
  • for matrix exponentials, you should use Matrix::expm() rather than expm::expm()
  • RTMB is pickier than R about matrix types. You may need to use some combination of drop() and as.matrix() to convert matrices with dimension 1 in some direction (or Matrix matrices) back to vectors
  • [[-indexing may be much faster than [-indexing: see here (and later messages in that thread)
  • if you use cat() or print() to print out numeric values, the results may not make sense (you’ll see a printout of RTMB’s internal representation of autodiff-augmented numbers …)

if transitioning from TMB

  • RTMB uses %*% (as in base R), not * (as in C++) for matrix/matrix and matrix/vector multiplication

more general points

  • as in C++ code, you probably don’t have to worry as much about non-vectorized code (e.g. for loops) being very slow relative to vectorized codes. However, vectorized code builds the model object (i.e. runs MakeADFun) much faster. In addition, you can control vectorized RTMB code to use more efficient internal operations e.g. by setting TapeConfig(vectorize="enable"). Comparing natively vectorized code (fastest) to RTMB vectorized or for-loop code (both a bit slower, but not different from each other) to native R for-loops (much slower):

  • data handling (see here, here) (and very similar arguments from 2004 about MLE fitting machinery taking a data argument)
  • have to handle prediction, tests, diagnostics, etc. etc. yourself (although the broom.mixed package does have a rudimentary tidy method for TMB objects (e.g. class(TMBobj) <- c("TMB", class(TMBobj)); broom.mixed::tidy(TMBobj))
  • if you do something clever where you define your objective function in a different environment from where you call MakeADFun, you can use assign(..., environment(objective_function)) to make sure that the objective function can see any objects it needs to know about …