Alex
Alex

Reputation: 15738

How do I include more objective functions in my TMB .cpp file?

The TMB objective functions are seemingly defined in one function block that is saved to a <name>.cpp file. Then, after compiling the file, each objective function is accessed by loading with the command dyn.load(dynlib(<name>)).

Is it possible to store more than one objective function in each .cpp file? For example, the following two objective functions are very similar to each other, but at the moment need to be saved to different files:

// TMB Tutorial but with fixed variance
#include <TMB.hpp>                                // Links in the TMB libraries

template<class Type>
Type objective_function<Type>::operator() ()
{
    DATA_VECTOR(x);                                 // Data vector transmitted from R
    PARAMETER(mu);                                  // Parameter value transmitted from R
    Type sigma = 1.0;

    Type f;                                         // Declare the "objective function" (neg. log. likelihood)
    f = -sum(dnorm(x,mu,sigma,true));               // Use R-style call to normal density

    return f;
}

and

// TMB Tutorial
#include <TMB.hpp>                                // Links in the TMB libraries

template<class Type>
Type objective_function<Type>::operator() ()
{
    DATA_VECTOR(x);                                 // Data vector transmitted from R
    PARAMETER(mu);                                  // Parameter value transmitted from R
    PARAMETER(sigma);                               //                 

    Type f;                                         // Declare the "objective function" (neg. log. likelihood)
    f = -sum(dnorm(x,mu,sigma,true));               // Use R-style call to normal density

    return f;
}

Upvotes: 2

Views: 630

Answers (2)

Alex
Alex

Reputation: 15738

I just wanted to make clear on what @alexforrence meant by

For unalike objective functions (not subsets of one another), you could pass a DATA_INTEGER or DATA_STRING into the template, e.g. like they did in glmmTMB here, and pick the objective function depending on the value of that DATA_*

It turns out there is a code snippet on the TMB github that covers this scenario, which I duplicate here:

#include <TMB.hpp>

template<class Type>
Type objective_function<Type>::operator() ()
{
  DATA_STRING(model_type);
  if (model_type == "model1") {
    #include "model1.h"
  } else
  if (model_type == "model2") {
    #include "model2.h"
  } else {
    error ("Unknown model type")
  }
  return 0;
}

That is, pass in a string telling the objective function which model to choose, and then include the text for that function stored in a separate .h file.

Upvotes: 0

alexforrence
alexforrence

Reputation: 2744

The "map" argument to MakeADFun() allows you to fix parameters at specific values.

In this example, we only need to compile/load the latter template. First, we'll write the template to a file, compile, and load the resulting DLL.

library(TMB)

file_conn <- file('test.cpp')

writeLines("
#include <TMB.hpp>                                

template<class Type>
Type objective_function<Type>::operator() ()
{
    DATA_VECTOR(x);                                 
    PARAMETER(mu);                                  
    PARAMETER(sigma);                               

    Type f;                                         
    f = -sum(dnorm(x,mu,sigma,true));               

    return f;
}
", file_conn)
close(file_conn)

compile('test.cpp')
dyn.load(dynlib('test'))

We can use the same DLL to fit models with and without a varying sigma.

n <- 100
x <- rnorm(n = n, mean = 0, sd = 1) 

f1 <- MakeADFun(data = list(x = x),
               parameters = list(mu = 0, sigma = 1),
               DLL = 'test')

f2 <- MakeADFun(data = list(x = x),
                parameters = list(mu = 0, sigma = 1),
                DLL = 'test',
                map = list(sigma = factor(NA)))

opt1 <- do.call('optim', f1)
opt2 <- do.call('optim', f2)

When using "map", the specified parameter(s) (sigma in this case) is fixed at the value given in "parameters".

Post-optimization, we do a sanity check -- the mus ought to be nearly identical.

> opt1$par
        mu      sigma 
0.08300554 1.07926521 
> opt2$par
        mu 
0.08300712 

Turning random effects on and off is a little more difficult. An example of that is given here, where you can use CppAD::Variable() to check whether to decrement the negative log-likelihood or not.

For unalike objective functions (not subsets of one another), you could pass a DATA_INTEGER or DATA_STRING into the template, e.g. like they did in glmmTMB here, and pick the objective function depending on the value of that DATA_*.

Upvotes: 2

Related Questions