Cannot covert CppAD::AD<double> and double

32 views
Skip to first unread message

Russell Almond

unread,
Mar 29, 2025, 6:12:22 PM3/29/25
to nimble-users
I'm having trouble compiling a model which I think has something with how I'm declaring something.  The idea is that observations by various persons and time points are coming from different distributions, so I've set up an array `tasks[i,it]` (passed as a constant) saying which one to use at each time point.  I've also set up a function list to do the indirection:

```
Irt <- nimbleFunctionVirtual(
  run = function(theta=double(0)) {returnType(double(1,3))}
)
IrtItemPool <- nimbleFunction(
  setup = function (irtmod,plist) {
    J <- length(plist)
    itemfuns <- nimbleFunctionList(Irt)
    for (task in 1:J)
        itemfuns[[task]] <- do.call(irtmod,plist[task])
    setupOutputs(J,itemfuns)
  },
  run = function(task=integer(0),theta=double(0)) {
    returnType(double(1,3))
    return(itemfuns[[task]]$run(theta))
  })
```

So the idea is that `theta` is the critical parameter, and the `ItemPool` function selects the `ItemFunction` according to the discrete task argument.

Here is the model code:

```
constGrowthCode <- nimbleCode({
  for (i in 1L:N) {
    theta[i,1L] ~ dnorm(mu0,sig0)
    for (it in 2L:(NT)) {
      theta[i,it] ~dnorm(theta[i,it-1L] + growthRate, growthSD)
    }
  }
 
  for (i in 1L:N) {
    for (it in 1L:NT) {
      p[i,it,1L:3L] <- itempool$run(tasks[i,it],theta[i,it])
      y[i,it] ~dcat(p[i,it,])
    }
  }
# Priors omitted
)
```
However, this gives me an error:
```
P_16_constGrowt_MID_27_nfCode.cpp:1092:42: error: cannot convert ‘TYPE_’ {aka ‘CppAD::AD<double>’} to ‘double’ 1092 | Interm_690 = (*itempool).run(Interm_688, Interm_689); | ^~~~~~~~~~ | | | TYPE_ {aka CppAD::AD<double>}
```

I don't understand how the compiler is deciding whether to map a variable to a double or an AD<double>. Does this have something to do with the automatic differentiation?


Perry de Valpine

unread,
Mar 30, 2025, 12:14:13 PM3/30/25
to Russell Almond, nimble-users
Hi Russell,

I think the most proximate issue is that IrtItemPool should have enableDerivs  turned on for run. I think also that task should be declared as double, not integer, because all model variables are doubles. It doesn’t usually matter, but for AD (yes, that is the issue) it does. And I think you’ll need to check out ADbreak to stop derivative tracking of task inside run. There is a subsection of the AD chapter of the user manual that tries to cover this. I am away from my computer so can’t check on other things that might come up using AD with nimbleFunctionVirtual. I think since task is constant, another option would to use if-then cases within IrtItemPool$run to call other nimbleFunctions, also with enableDerivs set.

HTH,
Perry



--
You received this message because you are subscribed to the Google Groups "nimble-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to nimble-users...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/nimble-users/c29e092d-aadb-4d2f-b855-0d1ab894754dn%40googlegroups.com.

Russell Almond

unread,
Mar 31, 2025, 11:21:47 AM3/31/25
to nimble-users
I was guessing the problem was related to automatic derivatives, thanks for the confirmation.

With a little bit of trial and error, I figured out I needed to put the manual derivatives in both the `IrtItemPool` function and the individual model functions.

```
IrtItemPool <- nimbleFunction(
  setup = function (irtmod,plist) {
    J <- length(plist)
    itemfuns <- nimbleFunctionList(Irt)
    if (length(irtmod)==1 || is.function(irtmod)) {
      for (task in 1:length(plist))

        itemfuns[[task]] <- do.call(irtmod,plist[task])
    } else {
      for (task in 1:length(plist))
        itemfuns[[task]] <- do.call(irtmod[[task]],plist[task])

    }
    setupOutputs(J,itemfuns)
  },
  run = function(task=integer(0),theta=double(0)) {
    returnType(double(1,3))
    return(itemfuns[[task]]$run(theta))
  },
  methods=list(
    derivsRun = function(task=integer(0),theta=double(0)) {
      return(derivs(run(task,theta),wrt="theta"))
      returnType(ADNimbleList())
    }
  ),
  buildDerivs="run"
)

Irt2pgr <- nimbleFunction(
  contains = Irt,
  setup = function(params) {
    a <- params[1L]
    b <- c(-999,params[2L:3L],999)
    setupOutputs(a,b)
  },
  run = function (theta=double(0L)) {
    et <- ilogit(1.7*a*theta-b)
    returnType(double(1L,3))
    return(et[1L:3L]-et[2L:4L])
  },
  methods = list(
    derivsRun = function(theta=double(0)) {
      return(derivsRun(theta),wrt="theta")
      returnType(ADNimbleList())
    }
  ),
  buildDerivs="run"
)
```

I think this was working, but I ran into another problem that my function is vector valued.  I'll need a different approach (as the output is put into `dcat`, I need to build this as a distribution rather than a plain function.)

Hopefully, my struggles will help somebody with a similar issue.
  --Russell
Reply all
Reply to author
Forward
0 new messages