nimble-dev / nimble

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

"Error in parse: unexpected end of input" when reading BUGS model file with `readBUGSmodel()` #1351

Closed pehkawn closed 11 months ago

pehkawn commented 1 year ago

I am currently testing some structural equation models from the book "Structural Equation Modeling: a Bayesian Approach" by Sik-Yum Lee (2007). The book provides WinBUGS codes and datasets for some of the models in the book, and I am currently trying to fit a model from chapter 6.6.2 using Nimble. The model has been tested, and works with WinBUGS, OpenBUGS and JAGS, where it runs without issue. (Since this model uses WinBUGS' I() function, this was changed to T() to work with JAGS.) However, when I try read in the model in Nimble, using the readBUGSmodel() function, I get the following parsing error:

Error in parse(text = modelText): <text>:45:0: unexpected end of input
43:         for(k in 1:4) { u[k]<-0  
44: }
   ^

The error indicates that there's a bracket or parenthesis missing, or similar. However, going through the file I can find no indication of such, and the code runs without issue in the aforementioned programs. Therefore I assume there must be something about the way readBUGSmodel() reads the file.

R code:

model <- readBUGSmodel(
     '/path/to/file/ch6-JAGS-model.txt'
)

BUGS code in file ch6-JAGS-model.txt:

model {
    for(i in 1:N){
        #measurement equation model
        for(j in 1:P){
            y[i, j] ~ dnorm(mu[i, j], psi[j])T(thd[j, z[i, j]], thd[j, z[i, j] + 1])
            ephat[i, j] <- y[i, j] - mu[i, j]
        }
        mu[i, 1] <- eta[i]
        mu[i, 2] <- lam[1] * eta[i]
        mu[i, 3] <- xi[i, 1]
        mu[i, 4] <- lam[2] * xi[i, 1]
        mu[i, 5] <- lam[3] * xi[i, 1]
        mu[i, 6] <- lam[4] * xi[i, 1]
        mu[i, 7] <- lam[5] * xi[i, 1]
        mu[i, 8] <- lam[6] * xi[i, 1]
        mu[i, 9] <- lam[7] * xi[i, 1]
        mu[i, 10] <- xi[i, 2]
        mu[i, 11] <- lam[8] * xi[i, 2]
        mu[i, 12] <- lam[9] * xi[i, 2]
        mu[i, 13] <- lam[10] * xi[i, 2]
        mu[i, 14] <- lam[11] * xi[i, 2]
                mu[i, 15] <- lam[12] * xi[i, 2]
        mu[i, 16] <- xi[i, 3]
        mu[i, 17] <- lam[13] * xi[i, 3]
        mu[i, 18] <- lam[14] * xi[i, 3]
        mu[i, 19] <- xi[i, 4]
        mu[i, 20] <- lam[15] * xi[i, 4]
        mu[i, 21] <- lam[16] * xi[i, 4]
        mu[i, 22] <- lam[17] * xi[i, 4]
        mu[i, 23] <- lam[18] * xi[i, 4]
        mu[i, 24] <- lam[19] * xi[i, 4]
        mu[i, 25] <- lam[20] * xi[i, 4]
        mu[i, 26] <- lam[21] * xi[i, 4]

        #structural equation model
        xi[i, 1:4] ~ dmnorm(u[1:4], phi[1:4, 1:4])
        eta[i] ~ dnorm(nu[i], psd)
        nu[i] <- gam[1] * xi[i, 1] + gam[2] * xi[i, 2] + gam[3] * xi[i, 3] + gam[4] * xi[i, 4]
        dthat[i] <- eta[i]-nu[i]
    } # end of i

    for(k in 1:4) { u[k] <- 0.0 } # According to the error message, the problem lies with this line, but I can't see it.

    #priors on loadings and coefficients
        var.lam[1] <- 4.0 * psi[2]
        var.lam[2] <- 4.0 * psi[4]
        var.lam[3] <- 4.0 * psi[5]
        var.lam[4] <- 4.0 * psi[6]
        var.lam[5] <- 4.0 * psi[7]
        var.lam[6] <- 4.0 * psi[8]
        var.lam[7] <- 4.0 * psi[9]
        var.lam[8] <- 4.0 * psi[11]
        var.lam[9] <- 4.0 * psi[12]
        var.lam[10] <- 4.0 * psi[13]
        var.lam[11] <- 4.0 * psi[14]
        var.lam[12] <- 4.0 * psi[15]
        var.lam[13] <- 4.0 * psi[17]
        var.lam[14] <- 4.0 * psi[18]
        var.lam[15] <- 4.0 * psi[20]
        var.lam[16] <- 4.0 * psi[21]
        var.lam[17] <- 4.0 * psi[22]
        var.lam[18] <- 4.0 * psi[23]
        var.lam[19] <- 4.0 * psi[24]
        var.lam[20] <- 4.0 * psi[25]
        var.lam[21] <- 4.0 * psi[26]
        for(i in 1:21){lam[i] ~ dnorm(0.8, var.lam[i])} 

    var.gam <- 4.0 * psd
    gam[1] ~ dnorm(0.6, var.gam)
    gam[2] ~ dnorm(0.6, var.gam)    
    gam[3] ~ dnorm(0.4, var.gam)
    gam[4] ~ dnorm(0.4, var.gam)

    #priors on precisions
    for(j in 1:P){
        psi[j] ~ dgamma(10, 8)
        sgm[j] <- 1/psi[j]
    }
    psd ~ dgamma(10, 8)
    sgd <- 1/psd
    phi[1:4, 1:4] ~ dwish(R[1:4, 1:4],  30)
    phx[1:4, 1:4] <- inverse(phi[1:4, 1:4])
    #end of model
} 
pehkawn commented 1 year ago

Ok, so studying the "dyes" model, I get the impression readBUGSmodel() don't understand where the for loop ends. After adding ; at the end of the small for-loops on lines 42 and 66, parsing error is no longer returned. Interestingly, no parsing error is returned due to lack of semicolon behind the right braces for the for-loops on lines 2-40 and 4-7.

paciorek commented 1 year ago

Thanks for the report. The issue seems to be some odd behavior in R's parse handling the syntax. I'm not seeing the problem on quick glance, but we'll dig deeper into this to try to fix for the next release.

Here's a reduced example that also fails:

model {
    for(i in 1:N){
        mu[i, 1] <- eta[i]
    }
    for(k in 1:4) {
         u[k] <- 0.0
        } 
       var.lam[2] <- 4.0 * psi[4]
} 

Commenting out the var.lam[2] definition avoids the failure.

pehkawn commented 1 year ago

Thank you for your reply. I did not find a particular issue with var.lam[2], if that's what you mean, and commenting out that particular line did not remove the parsing error. Commenting out all the variables in between the for-loops did, however, remove the parsing error. For now, it seems the easiest solution is to add a semicolon behind the right braces.

paciorek commented 11 months ago

Ok, narrowing this down further the issue seems to be with having a line that starts with "var" (or "data") following a for loop. So this is an issue with nimble not parse.

This means the problem is with how we separate blocks (model vs. var vs data) of syntax in input files. I'll need to dig into mergeMultiLineStatements to fix this.

paciorek commented 11 months ago

Actually the issue was in processModelFile. Our regex in defining modelBlockRegEx was not sophisticated enough to realize that (var|data|const) after a } (e.g., from a for loop) was not the beginning of a block.

In branch fix_1351 I have a fix.

paciorek commented 11 months ago

Fixed by PR #1369