adcomp icon indicating copy to clipboard operation
adcomp copied to clipboard

MakeADFun() throws error when all values of a vector are fixed with map= argument

Open jeffeaton opened this issue 4 years ago • 2 comments

Hello,

I'm trying to use the map argument to MakeADFun to fix the values of an entire parameter vector. When I fix the entire vector using NA values, I get an error Error in retape(set.defaults = TRUE) : PARAMETER COMPONENT NOT A VECTOR!. It works as described if only some of the elements of the vector are set to NA.

Thanks, Jeff

Description:

TMB::MakeADFun(..., map = <>) argument throws the following error when an entire parameter vector is fixed to NA using the map argument to MakeADFun:

> map_arg3 <- list(x = factor(1:5))
> map_arg3$x[] <- NA
> obj3 <- MakeADFun(data = data, parameters = par, dll = dll,
+                   map = map_arg3)
Error in retape(set.defaults = TRUE) : PARAMETER COMPONENT NOT A VECTOR!

Reproducible Steps:

Below is an R script reproducing the issue. I found this while trying fix the values for an entire random effect while debugging convergence issues with a model.

library(TMB)

code <- '
#include <TMB.hpp>
template<class Type>
Type objective_function<Type>::operator() ()
{
  PARAMETER_VECTOR(x);
  PARAMETER_VECTOR(y);
  Type val(0.0);
  val -= dnorm(x, 0, 1, true).sum();
  val -= dnorm(y, 0, 1, true).sum();
  return val;
}
'

f <- tempfile(fileext = ".cpp")
writeLines(code, f)
TMB::compile(f)
dyn.load(TMB::dynlib(tools::file_path_sans_ext(f)))
dll <- basename(tools::file_path_sans_ext(f))

data <- list()
par <- list(x = numeric(5), y = numeric(5))
obj1 <- MakeADFun(data = data, parameters = par, dll = dll)

obj1$fn()
obj1$gr()

## Fix x[2:5] -- works fine
map_arg2 <- list(x = factor(1:5))
map_arg2$x[2:5] <- NA

obj2 <- MakeADFun(data = data, parameters = par, dll = dll,
                  map = map_arg2)

## Fix x[1:5] -- throws error:
##  "Error in retape(set.defaults = TRUE) : PARAMETER COMPONENT NOT A VECTOR!"
map_arg3 <- list(x = factor(1:5))
map_arg3$x[] <- NA
obj3 <- MakeADFun(data = data, parameters = par, dll = dll,
                  map = map_arg3)

Current Output:

Here's the full console output, culminating in the error message noted above:

> library(TMB)
> 
> code <- '
+ #include <TMB.hpp>
+ template<class Type>
+ Type objective_function<Type>::operator() ()
+ {
+   PARAMETER_VECTOR(x);
+   PARAMETER_VECTOR(y);
+   Type val(0.0);
+   val -= dnorm(x, 0, 1, true).sum();
+   val -= dnorm(y, 0, 1, true).sum();
+   return val;
+ }
+ '
> 
> f <- tempfile(fileext = ".cpp")
> writeLines(code, f)
> TMB::compile(f)
...
[1] 0
> dyn.load(TMB::dynlib(tools::file_path_sans_ext(f)))
> dll <- basename(tools::file_path_sans_ext(f))
> 
> data <- list()
> par <- list(x = numeric(5), y = numeric(5))
> obj1 <- MakeADFun(data = data, parameters = par, dll = dll)
> 
> obj1$fn()
[1] 9.189385
> obj1$gr()
outer mgc:  0 
     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,]    0    0    0    0    0    0    0    0    0     0
> 
> ## Fix x[2:5] -- works fine
> map_arg2 <- list(x = factor(1:5))
> map_arg2$x[2:5] <- NA
> 
> obj2 <- MakeADFun(data = data, parameters = par, dll = dll,
+                   map = map_arg2)
> 
> ## Fix x[1:5] -- throws error:
> ##  "Error in retape(set.defaults = TRUE) : PARAMETER COMPONENT NOT A VECTOR!"
> map_arg3 <- list(x = factor(1:5))
> map_arg3$x[] <- NA
> obj3 <- MakeADFun(data = data, parameters = par, dll = dll,
+                   map = map_arg3)
Error in retape(set.defaults = TRUE) : PARAMETER COMPONENT NOT A VECTOR!

Expected Output:

This line would compile and fix the values for parameter vector x.

map_arg3 <- list(x = factor(1:5))
map_arg3$x[] <- NA
obj3 <- MakeADFun(data = data, parameters = par, dll = dll,
                  map = map_arg3)

TMB Version:

TMB_1.7.18

R Version:

4.0.2

Operating System:

macOS Catalina 10.15.6

jeffeaton avatar Jan 16 '21 20:01 jeffeaton

This is not the right way to use maps because the factor levels are kept constant (fix by e.g. x=factor(x)). The documentation ?MakeADFun should probably be improved by saying something like:

nlevels(map$p) is the resulting number of parameters after the map has been applied.

In addition one could expect a better error message...

kaskr avatar Feb 05 '21 12:02 kaskr

Thanks very much for clarifying the usage. Confirming adding in map$x = droplevels(map$x) results in works as expected:

library(TMB)

code <- '
#include <TMB.hpp>
template<class Type>
Type objective_function<Type>::operator() ()
{
  PARAMETER_VECTOR(x);
  PARAMETER_VECTOR(y);
  Type val(0.0);
  val -= dnorm(x, 0, 1, true).sum();
  val -= dnorm(y, 0, 1, true).sum();
  return val;
}
'

f <- tempfile(fileext = ".cpp")
writeLines(code, f)
TMB::compile(f)
dyn.load(TMB::dynlib(tools::file_path_sans_ext(f)))
dll <- basename(tools::file_path_sans_ext(f))

par <- list(x = numeric(5), y = numeric(5))
data <- list()

map_arg <- list(x = factor(1:5))
map_arg$x[] <- NA
map_arg$x <- droplevels(map_arg$x)

obj <- MakeADFun(data = data, parameters = par, dll = dll,
                 map = map_arg)

Thanks, Jeff

jeffeaton avatar Feb 05 '21 22:02 jeffeaton