nimble-dev / nimble

The base NIMBLE package for R
http://R-nimble.org
BSD 3-Clause "New" or "Revised" License
155 stars 22 forks source link

nimbleModel class name too long #1039

Closed finleya closed 3 years ago

finleya commented 4 years ago

Hi, first thanks for this great software.

Using NIMBLE version 0.9.0 on linux I get the following error when moving from a model with 6 dcar_proper random effects to 7 dcar_proper random effects, i.e., the model builds, complies, and samples without error when I comment out the components for the 7th random effect, but the addition of the 7th random effect elicits the error below.

Rmodel <- nimbleModel(code, constants, data, inits) defining model... Adding adj, num as data for building model. building model... Error in @<-(*tmp*, ".xData", value = ) : class name too long in '@<-'

Here's the model

code <- nimbleCode({

for(i in 1:p){
    beta[i] ~ dnorm(0, sd = 100)
    tau[i] ~ dgamma(0.001, 0.001)
    gamma[i] ~ dunif(-1, 1)
}

sigma ~ dgamma(0.001, 0.001)

s1[1:N] ~ dcar_proper(mu[1:N], adj=adj[1:L], num=num[1:N], tau=tau[1], gamma=gamma[1])
s2[1:N] ~ dcar_proper(mu[1:N], adj=adj[1:L], num=num[1:N], tau=tau[2], gamma=gamma[2])
s3[1:N] ~ dcar_proper(mu[1:N], adj=adj[1:L], num=num[1:N], tau=tau[3], gamma=gamma[3])
s4[1:N] ~ dcar_proper(mu[1:N], adj=adj[1:L], num=num[1:N], tau=tau[4], gamma=gamma[4])
s5[1:N] ~ dcar_proper(mu[1:N], adj=adj[1:L], num=num[1:N], tau=tau[5], gamma=gamma[5])
s6[1:N] ~ dcar_proper(mu[1:N], adj=adj[1:L], num=num[1:N], tau=tau[6], gamma=gamma[6])
s7[1:N] ~ dcar_proper(mu[1:N], adj=adj[1:L], num=num[1:N], tau=tau[7], gamma=gamma[7])
#s8[1:N] ~ dcar_proper(mu[1:N], adj=adj[1:L], num=num[1:N], tau=tau[8], gamma=gamma[8])
#s9[1:N] ~ dcar_proper(mu[1:N], adj=adj[1:L], num=num[1:N], tau=tau[9], gamma=gamma[9])
## s10[1:N] ~ dcar_proper(mu[1:N], adj=adj[1:L], num=num[1:N], tau=tau[10], gamma=gamma[10])
## s11[1:N] ~ dcar_proper(mu[1:N], adj=adj[1:L], num=num[1:N], tau=tau[11], gamma=gamma[11])
## s12[1:N] ~ dcar_proper(mu[1:N], adj=adj[1:L], num=num[1:N], tau=tau[12], gamma=gamma[12])

for(i in 1:n) {
    mu[i] <- 0
    y[i] ~ dnorm(x[i,1]*(beta[1] + s1[poly.id[i]]) + x[i,2]*(beta[2] + s2[poly.id[i]]) + x[i,3]*(beta[3] + s3[poly.id[i]]) +
                 x[i,4]*(beta[4] + s4[poly.id[i]]) + x[i,5]*(beta[5] + s5[poly.id[i]]) + x[i,6]*(beta[6] + s6[poly.id[i]]) +
                 x[i,7]*(beta[7] + s7[poly.id[i]]),# + x[i,8]*(beta[8] + s8[poly.id[i]]) + x[i,9]*(beta[9] + s9[poly.id[i]]) +
                 #x[i,10]*(beta[10] + s10[poly.id[i]]) + x[i,11]*(beta[11] + s11[poly.id[i]]) + x[i,12]*(beta[12] + s12[poly.id[i]]),
                 sd = sigma)
}

})

N <- length(num) L <- length(adj)

constants <- list(poly.id = poly.id, adj = adj, num = num, n = n, p = p, N = N, L = L)

data <- list(y = y, x = x)

inits <- list(sigma = 0.01, beta = beta, tau = rep(1, p), gamma = rep(0.5, p), s1 = rep(0, N), s2 = rep(0, N), s3 = rep(0, N), s4 = rep(0, N), s5 = rep(0, N), s6 = rep(0, N), s7 = rep(0, N))#, s8 = rep(0, N)), s9 = rep(0, N)),

s10 = rep(0, N), s11 = rep(0, N), s12 = rep(0, N))

params <- c("beta", "tau", "gamma", "sigma", "s1", "s2", "s3", "s4", "s5", "s6", "s7")#, "s8", "s9", "s10", "s11", "s12")

Rmodel <- nimbleModel(code, constants, data, inits)

perrydv commented 4 years ago

Hi @finleya Thanks for filing this issue. We'll look into it. In the meantime, let me suggest a workaround. I think if you break up your long sum into a couple of terms and then add those together, the problem will be avoided. i.e. something like

part1[i] <- x[i,1]*(beta[1] + s1[poly.id[i]]) + x[i,2]*(beta[2] + s2[poly.id[i]])
part2[i] <- x[i,3]*(beta[3] + s3[poly.id[i]]) + x[i,4]*(beta[4] + s4[poly.id[i]])
y[i] ~ dnorm( part1[i] + part2[i], sd = sigma)

(where obviously I haven't included all the terms you want in part1[i] and part2[i]). Fingers crossed.

perrydv commented 4 years ago

@finleya It could potentially also help to provide the name argument to nimbleModel. Just trying to give some quick things to try until we have time to take a look.

finleya commented 4 years ago

Hi Perry,

Thank you very much for the ultra quick response and fix! I had tried passing the name argument without success, but your "part" approach worked. Again, thanks to you and the NIMBLE team for this fantastic software!

perrydv commented 4 years ago

Thanks @finleya. Phew, glad we got a workaround for you. If you don't mind, I'm going to reopen the issue so that the dev team remembers to look into it further. It's not very friendly behavior!

paciorek commented 4 years ago

see RnameToCppName - if too long, truncate and add on _uID and use labelmaker (all_utils.R). (Make a new labelmaker for htis case).

paciorek commented 3 years ago

@perrydv This is more complicated than I anticipated. I see that the same strings are being passed through Rname2CppName repeatedly and we are expecting that the resulting names in a pair of .h and .cpp files are expected to be the same.

But if I truncate and then tack on additional characters to make the result unique, when it gets passed through Rname2CppName again it gets truncated again and a new unique label is tacked on, which results in inconsistent names between the .h and .cpp files.

I could truncate with some additional chopping off as a buffer, so the resulting truncated name, even after having additional characters tacked on, is not subject to the truncation. Feels kludgey as I don't know for sure how many characters will maximally be tacked on, but I can presumably be generous.

However, it does feel like Rname2CppName is being used in cases where it doesn't need to be, given that after an initial usage in building the model, I don't think that any problematic characters (e.g., brackets) would remain in any of the names of things in the R model. Nor do I see why usage of deparse would be needed. So given the only operations are deparse and gsub, usage of Rname2CppName in compiling is surprising me, but it's likely there is a need for it that I am not seeing.

paciorek commented 3 years ago

Per Perry's suggestion, I added a check that only does truncation if hasn't been truncated before, based on grepping for "___TRUNC___", which is the string I used in the label maker. The use of three underscores is because we have cases where we insert two underscores, so this felt like the best way to ensure one would not have the string occur except via insertion under these circumstances.