mjskay / metabayes

R package for specifying Bayesian models (JAGS, Stan) as R code

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

If else statement works inappropriately

MillyLiu87 opened this issue · comments

For testing if-else statement in Metabayes package, the following steps were carried out:

  1. model
    model = metajags(
    model = {
    #core model
    for (i in 1:n) {
    if(x[i] >= 0)
    {
    y[i] <- 3_x[i]
    }
    else{
    y[i] <- x[i]
    }
    }
    }
    )
  2. data
    x = sample(-2:2, 20, replace = TRUE)
    y = 2_x
    n = 20
  3. run
    jags = run.jags(code(model), data = list(n = n, x = x), n.chains=2, "y")
  4. result
    One samples for x could be
    -2 0 1 2 2 0 2 2 -2 2 2 -2 -2 2 1 -2 2 1 0 1
    And using the model, the samples for y would be
    -6 0 3 6 6 0 6 6 -6 6 6 -6 -6 6 3 -6 6 3 0 3
    It seems the else statement was not executed.
    It is claimed that the R expression in if condition is evaluated immediately when the Metajags specification is compiled. The question would be which value in x is evaluated?

Thanks for the bug report!

In fact, the results are consistent with how the if/else statement is intended to work in metajags. The if/else statement, unlike the ifelse function, is evaluated only once: when the metajags code is compiled into JAGS code. It seems like you might be looking for functionality provided by the ifelse function. I will explain with an example.

First, let's run the code you have provided in a fresh environment:

library(metabayes)
library(runjags)

# we will remove any existing objects from the R environment so that
# the metajags call is evaluated "fresh"
rm(list = ls())

model = metajags(
    model = {
        #core model
        for (i in 1:n) {
            if(x[i] >= 0)
            { 
                y[i] <- 3 * x[i]
            }
            else{
                y[i] <- x[i]
            }
        }
    }
)
## Error in eval(expr, envir, enclos) : object 'x' not found

The code failed because metajags tried to evaluate expression x[i] >= 0 in R (not JAGS) at the moment of compilation, and x has not been defined yet. To get the code to run, we can define x first:

x = sample(-2:2, 20, replace = TRUE)
i = 1
x
##  [1] -2  2  0 -2 -1 -1 -2  0  1 -2  1 -2 -2  2  0  1 -2  2  2 -1
model = metajags(
    model = {
        #core model
        for (i in 1:n) {
            if(x[i] >= 0)
            { 
                y[i] <- 3 * x[i]
            }
            else{
                y[i] <- x[i]
            }
        }
    }
)

The metajags code now compiles because we have defined x and i, so metajags can evaluate x[i] >= 0. However, it will only evaluate this once, and then substitute the appropriate branch into the JAGS code. We can see this by looking at the compiled JAGS code:

model
## metajags code:
## 
## 1     model {
## 2         for (i in 1 : n) {
## 3             
## 4             y[i] <- x[i];
## 5         }

Since the first element of x is less than 0, x[i] <= 0 evaluates to FALSE and the if/else statement is replaced with the contents of the else branch, y[i] <- x[i] (had x[1] been greater than or equal to 0, this would have been y[i] <- 3 * x[i]).

The if/else statement is intended for use in meta-programming: e.g., if you want to use R variables and expressions to choose between different specifications for parts of your model, you might use the if/else statement. However, in your case, I think you are looking for the ifelse function, which is evaluated during sampling and not at compile time.

For example, I think you want something like the following model:

model = metajags(
    model = {
        #core model
        for (i in 1:n) {
            y[i] <- ifelse(x[i] >= 0, 3 * x[i], x[i])
        }
    }
)

We can inspect the model to see that ifelse is not evaluated at compile time:

model
## metajags code:
##
## 1     model {
## 2         for (i in 1 : n) {
## 3             y[i] <- ifelse(x[i] >= 0,3 * x[i],x[i]);
## 4         }
## 5     }

We can run the model:

n = length(x)
jags = run.jags(code(model), data = list(n = n, x = x), n.chains=2, "y")
## Compiling rjags model...
## Calling the simulation using the rjags method...
## Note: the model did not require adaptation
## Burning in the model for 4000 iterations...
## 
##   |                                                        
##   |**************************************************| 100%
## Running the model for 10000 iterations...
## 
##   |                                                        
##   |**************************************************| 100%
## Simulation complete
## Calculating summary statistics...
## Note: The monitored variables 'y[1]', 'y[2]', 'y[3]', 'y[4]', 'y[5]',
## 'y[6]', 'y[7]', 'y[8]', 'y[9]', 'y[10]', 'y[11]', 'y[12]', 'y[13]',
## 'y[14]', 'y[15]', 'y[16]', 'y[17]', 'y[18]', 'y[19]' and 'y[20]' appear
## to be non-stochastic; they will not be included in the convergence
## diagnostic
## Finished running the simulation
## Warning message:
## No initial values were provided - JAGS will use the same initial values for all chains 

And then compare against the correct results:

correct_y = ifelse(x >= 0, 3 * x, x)
rbind(summary(jags)[,"Mean"], correct_y)
##           y[1] y[2] y[3] y[4] y[5] y[6] y[7] y[8] y[9] y[10] y[11] y[12] y[13]
##             -2    6    0   -2   -1   -1   -2    0    3    -2     3    -2    -2
## correct_y   -2    6    0   -2   -1   -1   -2    0    3    -2     3    -2    -2
##           y[14] y[15] y[16] y[17] y[18] y[19] y[20]
##               6     0     3    -2     6     6    -1
## correct_y     6     0     3    -2     6     6    -1