Skip to content

Latest commit

 

History

History
1033 lines (873 loc) · 41.7 KB

inla-internals.md

File metadata and controls

1033 lines (873 loc) · 41.7 KB

This document is to record the details of some of the internal specifications of INLA. The purpose of this is largely to document what INLA is doing for comparing model implementations in other software.

Make sure to be using a version of INLA more recent than INLA_20.06.15 testing version when the internal implementation of the RW1 and RW2 models was updated to be consistent with manual model scaling.

library(INLA)

Data

Create some very simple space-time data for testing INLA. Data are for four areas in which area 2 is connected to all other areas, areas 1 and 3 are connected, and area 4 is connected only to area 2. Three time points are simulated.

set.seed(1)
data <- expand.grid(area = 1:4,
                    time = 1:3)
                    
data$y <- rpois(nrow(data), 2.5)

adj <- rbind(c(0, 1, 1, 0),
             c(0, 0, 1, 1),
             0,
             0)
adj <- adj + t(adj)
rownames(adj) <- colnames(adj) <- letters[1:4]

adj
##   a b c d
## a 0 1 1 0
## b 1 0 1 1
## c 1 1 0 0
## d 0 1 0 0

Structure matrices for ICAR area effect and RW1 time effects.

R_area <- diag(rowSums(adj)) - adj
R_time <- t(diff(diag(3))) %*% diff(diag(3))

R_area
##    a  b  c  d
## a  2 -1 -1  0
## b -1  3 -1 -1
## c -1 -1  2  0
## d  0 -1  0  1
R_time
##      [,1] [,2] [,3]
## [1,]    1   -1    0
## [2,]   -1    2   -1
## [3,]    0   -1    1

Details of INLA internals

This section documents details of how certain arguments and options are implemented by INLA such as how the small constant is added to the diagonal, model scaling, and kronecker product for the f(..., group = <>) option.

The strategy for checking is to create a ‘null’ data set with no observations and ‘fit’ the model with fixed values for the hyper parameters to recover the Q matrix constructed by INLA.

datanull <- data[data$area == 1, ]
datanull$y <- NA

hyper_area <- list(prec = list(initial = log(1), fixed = TRUE))
hyper_time <- list(prec = list(initial = log(1), fixed = TRUE))

ICAR model

fit1 <- inla(y ~ 0 +
               f(area, model = "besag", graph = adj, hyper = hyper_area),
             data = datanull, family = "poisson",
             control.inla = list(strategy = "gaussian", int.strategy = "eb"),
             control.fixed = list(mean.intercept = 0, prec.intercept = 1),
             control.compute = list(config = TRUE))

Extra constant added to diagonal

  • The default value for the diagonal extra constant is 1.0151131^{-5}.
    • This is ascertained from INLA:::inla.set.f.default()$diagonal.
  • This is added after scaling the matrix by the precision parameter.

To see this, in fit1 the fixed value for the precision is 1.0 and in fit2 the value for the precision is 2.0. The added value along the diagonal in both cases is 1e-5.

hyper_area2 <- list(prec = list(initial = log(2), fixed = TRUE))

fit2 <- inla(y ~ 0 +
               f(area, model = "besag", graph = adj, hyper = hyper_area2),
             data = datanull, family = "poisson",
             control.inla = list(strategy = "gaussian", int.strategy = "eb"),
             control.fixed = list(mean.intercept = 0, prec.intercept = 1),
             control.compute = list(config = TRUE))

fit1$misc$configs$config[[1]]$Q[-(1:3), -(1:3)]
## 4 x 4 sparse Matrix of class "dgCMatrix"
##                                         
## [1,] 488266.4 -1.00000 -1.00000  .      
## [2,]      .    3.00001 -1.00000 -1.00000
## [3,]      .    .        2.00001  .      
## [4,]      .    .        .        1.00001
fit2$misc$configs$config[[1]]$Q[-(1:3), -(1:3)]
## 4 x 4 sparse Matrix of class "dgCMatrix"
##                                         
## [1,] 488268.4 -2.00000 -2.00000  .      
## [2,]      .    6.00001 -2.00000 -2.00000
## [3,]      .    .        4.00001  .      
## [4,]      .    .        .        2.00001

Application of f(..., scale.model = TRUE)

When argument scale.model = TRUE, the precision matrix is scaled so that the generalised variance is 1.

  • A sum-to-zero constraint is applied in the model scaling. (Likely a different constraint is applied if there are multiple connected components).
  • No constant is added to the diagonal before model scaling.
fit <- inla(y ~ 0 +
              f(area, model = "besag", graph = adj, hyper = hyper_area,
                scale.model = TRUE),
            data = datanull, family = "poisson",
            control.inla = list(strategy = "gaussian", int.strategy = "eb"),
            control.fixed = list(mean.intercept = 0, prec.intercept = 1),
            control.compute = list(config = TRUE))

fit$misc$configs$constr
## $nc
## [1] 1
## 
## $A
##      [,1] [,2] [,3] [,4] [,5] [,6] [,7]
## [1,]    0    0    0    1    1    1    1
## 
## $e
## [1] 0
fit$misc$configs$config[[1]]$Q[-(1:3), -(1:3)]
## 4 x 4 sparse Matrix of class "dgCMatrix"
##                                               
## [1,] 488265.1 -0.3565926 -0.3565926  .        
## [2,]      .    1.0697879 -0.3565926 -0.3565926
## [3,]      .    .          0.7131953  .        
## [4,]      .    .          .          0.3566028
inla.scale.model(R_area, constr = list(A = matrix(1, ncol = 4), e = 0))
## 4 x 4 sparse Matrix of class "dgTMatrix"
##            a          b          c          d
## a  0.7131852 -0.3565926 -0.3565926  .        
## b -0.3565926  1.0697778 -0.3565926 -0.3565926
## c -0.3565926 -0.3565926  0.7131852  .        
## d  .         -0.3565926  .          0.3565926
  • Even if constr = FALSE or an alternative constraint is specified in the f() object, the same sum-to-zero constraint is applied to the model scaling.
fit_no_constr <- inla(y ~ 0 +
             f(area, model = "besag", graph = adj, hyper = hyper_area,
               scale.model = TRUE, constr = FALSE),
           data = datanull, family = "poisson",
           control.inla = list(strategy = "gaussian", int.strategy = "eb"),
           control.fixed = list(mean.intercept = 0, prec.intercept = 1),
           control.compute = list(config = TRUE))

fit_no_constr$misc$configs$constr
## NULL
fit_no_constr$misc$configs$config[[1]]$Q[-(1:3), -(1:3)]
## 4 x 4 sparse Matrix of class "dgCMatrix"
##                                               
## [1,] 488265.1 -0.3565926 -0.3565926  .        
## [2,]      .    1.0697779 -0.3565926 -0.3565926
## [3,]      .    .          0.7131853  .        
## [4,]      .    .          .          0.3565926
fit_alt_constr <- inla(y ~ 0 +
              f(area, model = "besag", graph = adj, hyper = hyper_area,
                scale.model = TRUE, constr = FALSE,
                diagonal = 0,
                extraconstr = list(A = matrix(c(1, 1, 0, 0), 1), e = 3)),
            data = datanull, family = "poisson",
            control.inla = list(strategy = "gaussian", int.strategy = "eb"),
            control.fixed = list(mean.intercept = 0, prec.intercept = 1),
            control.compute = list(config = TRUE))

fit_alt_constr$misc$configs$constr
## $nc
## [1] 1
## 
## $A
##      [,1] [,2] [,3] [,4] [,5] [,6] [,7]
## [1,]    0    0    0    1    1    0    0
## 
## $e
## [1] 3
fit_alt_constr$misc$configs$config[[1]]$Q[-(1:3), -(1:3)]
## 4 x 4 sparse Matrix of class "dgCMatrix"
##                                               
## [1,] 488265.1 -0.3565926 -0.3565926  .        
## [2,]      .    1.0697779 -0.3565926 -0.3565926
## [3,]      .    .          0.7131853  .        
## [4,]      .    .          .          0.3565926

Called externally, the alternative constraint does slightly affect model scaling, and so the above confirms that the alternative constraint is not used in the scale.model specification.

inla.scale.model(R_area, constr = list(A = matrix(c(1, 1, 1, 1), ncol = 4), e = 0))
## 4 x 4 sparse Matrix of class "dgTMatrix"
##            a          b          c          d
## a  0.7131852 -0.3565926 -0.3565926  .        
## b -0.3565926  1.0697778 -0.3565926 -0.3565926
## c -0.3565926 -0.3565926  0.7131852  .        
## d  .         -0.3565926  .          0.3565926
inla.scale.model(R_area, constr = list(A = matrix(c(1, 1, 0, 0), ncol = 4), e = 3))
## 4 x 4 sparse Matrix of class "dgTMatrix"
##            a          b          c          d
## a  0.7135650 -0.3567825 -0.3567825  .        
## b -0.3567825  1.0703475 -0.3567825 -0.3567825
## c -0.3567825 -0.3567825  0.7135650  .        
## d  .         -0.3567825  .          0.3567825

Default behaviour of f(..., group = <var>)

Next we review the default behaviour of the grouping option to specify product smooths. The below fits a separable space-time model with an ICAR area effect and RW1 time effect, otherwise using defaults.

fit <- inla(y ~ 0 +
              f(area, model = "besag", graph = adj, hyper = hyper_area,
                group = time, control.group = list(model = "rw1")),
            data = datanull, family = "poisson",
            control.inla = list(strategy = "gaussian", int.strategy = "eb"),
            control.fixed = list(mean.intercept = 0, prec.intercept = 1),
            control.compute = list(config = TRUE))

Default constraints for grouped models

  • By default, the a separate sum-to-zero constraint is specified for each level of the group variable.
fit$misc$configs$constr
## $nc
## [1] 3
## 
## $A
##      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] [,14]
## [1,]    0    0    0    1    1    1    1    0    0     0     0     0     0     0
## [2,]    0    0    0    0    0    0    0    1    1     1     1     0     0     0
## [3,]    0    0    0    0    0    0    0    0    0     0     0     1     1     1
##      [,15]
## [1,]     0
## [2,]     0
## [3,]     1
## 
## $e
## [1] 0 0 0
  • If extraconstr= is specified, the constraint must be the length of the number of levels for the primary variable (e.g. 4 areas in the example below).
  • This constraint is repeated for each level of the group variable.
area_constr <- list(A = matrix(c(1.5, 0.5, 0, 0), 1), e = 3)

fit_alt_constr <- inla(y ~ 0 +
       f(area, model = "besag", graph = adj, hyper = hyper_area,
         group = time, control.group = list(model = "rw1"),
         constr = FALSE, extraconstr = area_constr),
     data = datanull, family = "poisson",
     control.inla = list(strategy = "gaussian", int.strategy = "eb"),
     control.fixed = list(mean.intercept = 0, prec.intercept = 1),
     control.compute = list(config = TRUE))

fit_alt_constr$misc$configs$constr
## $nc
## [1] 3
## 
## $A
##      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] [,14]
## [1,]    0    0    0  1.5  0.5    0    0  0.0  0.0     0     0   0.0   0.0     0
## [2,]    0    0    0  0.0  0.0    0    0  1.5  0.5     0     0   0.0   0.0     0
## [3,]    0    0    0  0.0  0.0    0    0  0.0  0.0     0     0   1.5   0.5     0
##      [,15]
## [1,]     0
## [2,]     0
## [3,]     0
## 
## $e
## [1] 3 3 3

As far as I can tell, there is no way to specify (1) a constraint for only some group levels, (2) different constraints for different group levels, or (3) constraints that span multiple group levels. For example, the following constraint with dimension 4 x 3 = 12 might be used to specify an overall sum-to-zero constraint (across all groups) on the space x time latent field.

wishful_constr <- list(A = matrix(1, ncol = 12), e = 0)
wishful_constr
## $A
##      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12]
## [1,]    1    1    1    1    1    1    1    1    1     1     1     1
## 
## $e
## [1] 0

But this throws an error because INLA is expecting the constraint to have dimension 4 (the number of areas) and will repeat this constraint multiple times.

inla(y ~ 0 +
       f(area, model = "besag", graph = adj, hyper = hyper_area,
         group = time, control.group = list(model = "rw1"),
         constr = FALSE, 
         extraconstr = wishful_constr),
     data = datanull, family = "poisson")
## Error in inla(y ~ 0 + f(area, model = "besag", graph = adj, hyper = hyper_area, : 
##  ncol in matrix A(extraconstr) does not correspont to the length of f: 12 4

More flexible constraints should be possible by specifying custom model using the "rgeneric" model type and manually specifying the Cmatrix as the Kronecker product.

Model scaling with grouped models

  • The control.group = list(...) default for scale.model is TRUE, even when the default for scale.model is FALSE.
  • The main term is not scaled, consistent with f(..., scale.model = ) default.
inla.getOption()$scale.model.default
## [1] FALSE

Model with defaults for scale model

fit <- inla(y ~ 0 +
             f(area, model = "besag", graph = adj, hyper = hyper_area,
               group = time, control.group = list(model = "rw1")),
            data = datanull, family = "poisson",
            control.inla = list(strategy = "gaussian", int.strategy = "eb"),
            control.fixed = list(mean.intercept = 0, prec.intercept = 1),
            control.compute = list(config = TRUE))

R_area_scaled <- inla.scale.model(R_area, 
                                  constr = list(A = matrix(1, ncol = 4), e = 0))
R_time_scaled <- inla.scale.model(R_time, 
                                  constr = list(A = matrix(1, ncol = 3), e = 0))

round(fit$misc$configs$config[[1]]$Q[-(1:3), -(1:3)], 5)
## 12 x 12 sparse Matrix of class "dgCMatrix"
##                                                                         
##  [1,] 162755.6 -0.40934 -0.40934  .           -0.81867  0.40934  0.40934
##  [2,]      .    1.22802 -0.40934 -0.40934      0.40934 -1.22801  0.40934
##  [3,]      .    .        0.81868  .            0.40934  0.40934 -0.81867
##  [4,]      .    .        .        0.40935      .        0.40934  .      
##  [5,]      .    .        .        .       162756.42878 -0.81867 -0.81867
##  [6,]      .    .        .        .            .        2.45603 -0.81867
##  [7,]      .    .        .        .            .        .        1.63736
##  [8,]      .    .        .        .            .        .        .      
##  [9,]      .    .        .        .            .        .        .      
## [10,]      .    .        .        .            .        .        .      
## [11,]      .    .        .        .            .        .        .      
## [12,]      .    .        .        .            .        .        .      
##                                                       
##  [1,]  .            .        .        .        .      
##  [2,]  0.40934      .        .        .        .      
##  [3,]  .            .        .        .        .      
##  [4,] -0.40934      .        .        .        .      
##  [5,]  .           -0.81867  0.40934  0.40934  .      
##  [6,] -0.81867      0.40934 -1.22801  0.40934  0.40934
##  [7,]  .            0.40934  0.40934 -0.81867  .      
##  [8,]  0.81868      .        0.40934  .       -0.40934
##  [9,]  .       162755.61010 -0.40934 -0.40934  .      
## [10,]  .            .        1.22802 -0.40934 -0.40934
## [11,]  .            .        .        0.81868  .      
## [12,]  .            .        .        .        0.40935

This matches the Kronecker product of the unscaled area structure matrix and scaled time structure matrix.

kronecker(R_time_scaled, R_area)
## 12 x 12 sparse Matrix of class "dgTMatrix"
##                                                                        
##  [1,]  0.8186736 -0.4093368 -0.4093368  .         -0.8186736  0.4093368
##  [2,] -0.4093368  1.2280105 -0.4093368 -0.4093368  0.4093368 -1.2280105
##  [3,] -0.4093368 -0.4093368  0.8186736  .          0.4093368  0.4093368
##  [4,]  .         -0.4093368  .          0.4093368  .          0.4093368
##  [5,] -0.8186736  0.4093368  0.4093368  .          1.6373473 -0.8186736
##  [6,]  0.4093368 -1.2280105  0.4093368  0.4093368 -0.8186736  2.4560209
##  [7,]  0.4093368  0.4093368 -0.8186736  .         -0.8186736 -0.8186736
##  [8,]  .          0.4093368  .         -0.4093368  .         -0.8186736
##  [9,]  .          .          .          .         -0.8186736  0.4093368
## [10,]  .          .          .          .          0.4093368 -1.2280105
## [11,]  .          .          .          .          0.4093368  0.4093368
## [12,]  .          .          .          .          .          0.4093368
##                                                                        
##  [1,]  0.4093368  .          .          .          .          .        
##  [2,]  0.4093368  0.4093368  .          .          .          .        
##  [3,] -0.8186736  .          .          .          .          .        
##  [4,]  .         -0.4093368  .          .          .          .        
##  [5,] -0.8186736  .         -0.8186736  0.4093368  0.4093368  .        
##  [6,] -0.8186736 -0.8186736  0.4093368 -1.2280105  0.4093368  0.4093368
##  [7,]  1.6373473  .          0.4093368  0.4093368 -0.8186736  .        
##  [8,]  .          0.8186736  .          0.4093368  .         -0.4093368
##  [9,]  0.4093368  .          0.8186736 -0.4093368 -0.4093368  .        
## [10,]  0.4093368  0.4093368 -0.4093368  1.2280105 -0.4093368 -0.4093368
## [11,] -0.8186736  .         -0.4093368 -0.4093368  0.8186736  .        
## [12,]  .         -0.4093368  .         -0.4093368  .          0.4093368

Fit both scaled f(..., scale.model = TRUE, ..., control.group = list(..., scale.model = TRUE))

fit <- inla(y ~ 0 +
              f(area, model = "besag", graph = adj, hyper = hyper_area,
                scale.model = TRUE,
                group = time,
                control.group = list(model = "rw1", scale.model = TRUE)),
            data = datanull, family = "poisson",
            control.inla = list(strategy = "gaussian", int.strategy = "eb"),
            control.fixed = list(mean.intercept = 0, prec.intercept = 1),
            control.compute = list(config = TRUE))

round(fit$misc$configs$config[[1]]$Q[-(1:3), -(1:3)], 5)
## 12 x 12 sparse Matrix of class "dgCMatrix"
##                                                                         
##  [1,] 162755.1 -0.14597 -0.14597  .           -0.29193  0.14597  0.14597
##  [2,]      .    0.43791 -0.14597 -0.14597      0.14597 -0.43790  0.14597
##  [3,]      .    .        0.29194  .            0.14597  0.14597 -0.29193
##  [4,]      .    .        .        0.14598      .        0.14597  .      
##  [5,]      .    .        .        .       162755.37530 -0.29193 -0.29193
##  [6,]      .    .        .        .            .        0.87581 -0.29193
##  [7,]      .    .        .        .            .        .        0.58388
##  [8,]      .    .        .        .            .        .        .      
##  [9,]      .    .        .        .            .        .        .      
## [10,]      .    .        .        .            .        .        .      
## [11,]      .    .        .        .            .        .        .      
## [12,]      .    .        .        .            .        .        .      
##                                                       
##  [1,]  .            .        .        .        .      
##  [2,]  0.14597      .        .        .        .      
##  [3,]  .            .        .        .        .      
##  [4,] -0.14597      .        .        .        .      
##  [5,]  .           -0.29193  0.14597  0.14597  .      
##  [6,] -0.29193      0.14597 -0.43790  0.14597  0.14597
##  [7,]  .            0.14597  0.14597 -0.29193  .      
##  [8,]  0.29194      .        0.14597  .       -0.14597
##  [9,]  .       162755.08336 -0.14597 -0.14597  .      
## [10,]  .            .        0.43791 -0.14597 -0.14597
## [11,]  .            .        .        0.29194  .      
## [12,]  .            .        .        .        0.14598
round(kronecker(R_time_scaled, R_area_scaled), 5)
## 12 x 12 sparse Matrix of class "dgTMatrix"
##                                                                              
##  [1,]  0.29193 -0.14597 -0.14597  .       -0.29193  0.14597  0.14597  .      
##  [2,] -0.14597  0.43790 -0.14597 -0.14597  0.14597 -0.43790  0.14597  0.14597
##  [3,] -0.14597 -0.14597  0.29193  .        0.14597  0.14597 -0.29193  .      
##  [4,]  .       -0.14597  .        0.14597  .        0.14597  .       -0.14597
##  [5,] -0.29193  0.14597  0.14597  .        0.58387 -0.29193 -0.29193  .      
##  [6,]  0.14597 -0.43790  0.14597  0.14597 -0.29193  0.87580 -0.29193 -0.29193
##  [7,]  0.14597  0.14597 -0.29193  .       -0.29193 -0.29193  0.58387  .      
##  [8,]  .        0.14597  .       -0.14597  .       -0.29193  .        0.29193
##  [9,]  .        .        .        .       -0.29193  0.14597  0.14597  .      
## [10,]  .        .        .        .        0.14597 -0.43790  0.14597  0.14597
## [11,]  .        .        .        .        0.14597  0.14597 -0.29193  .      
## [12,]  .        .        .        .        .        0.14597  .       -0.14597
##                                          
##  [1,]  .        .        .        .      
##  [2,]  .        .        .        .      
##  [3,]  .        .        .        .      
##  [4,]  .        .        .        .      
##  [5,] -0.29193  0.14597  0.14597  .      
##  [6,]  0.14597 -0.43790  0.14597  0.14597
##  [7,]  0.14597  0.14597 -0.29193  .      
##  [8,]  .        0.14597  .       -0.14597
##  [9,]  0.29193 -0.14597 -0.14597  .      
## [10,] -0.14597  0.43790 -0.14597 -0.14597
## [11,] -0.14597 -0.14597  0.29193  .      
## [12,]  .       -0.14597  .        0.14597

Fit main effect scaled and group unscaled f(..., scale.model = TRUE, ..., control.group = list(..., scale.model = FALSE)).

fit <- inla(y ~ 0 +
              f(area, model = "besag", graph = adj, hyper = hyper_area,
                scale.model = TRUE,
                group = time,
                control.group = list(model = "rw1", scale.model = FALSE)),
            data = datanull, family = "poisson",
            control.inla = list(strategy = "gaussian", int.strategy = "eb"),
            control.fixed = list(mean.intercept = 0, prec.intercept = 1),
            control.compute = list(config = TRUE))

round(fit$misc$configs$config[[1]]$Q[-(1:3), -(1:3)], 5)
## 12 x 12 sparse Matrix of class "dgCMatrix"
##                                                                         
##  [1,] 162755.5 -0.35659 -0.35659  .           -0.71319  0.35659  0.35659
##  [2,]      .    1.06979 -0.35659 -0.35659      0.35659 -1.06978  0.35659
##  [3,]      .    .        0.71320  .            0.35659  0.35659 -0.71319
##  [4,]      .    .        .        0.35660      .        0.35659  .      
##  [5,]      .    .        .        .       162756.21780 -0.71319 -0.71319
##  [6,]      .    .        .        .            .        2.13957 -0.71319
##  [7,]      .    .        .        .            .        .        1.42638
##  [8,]      .    .        .        .            .        .        .      
##  [9,]      .    .        .        .            .        .        .      
## [10,]      .    .        .        .            .        .        .      
## [11,]      .    .        .        .            .        .        .      
## [12,]      .    .        .        .            .        .        .      
##                                                       
##  [1,]  .            .        .        .        .      
##  [2,]  0.35659      .        .        .        .      
##  [3,]  .            .        .        .        .      
##  [4,] -0.35659      .        .        .        .      
##  [5,]  .           -0.71319  0.35659  0.35659  .      
##  [6,] -0.71319      0.35659 -1.06978  0.35659  0.35659
##  [7,]  .            0.35659  0.35659 -0.71319  .      
##  [8,]  0.71320      .        0.35659  .       -0.35659
##  [9,]  .       162755.50461 -0.35659 -0.35659  .      
## [10,]  .            .        1.06979 -0.35659 -0.35659
## [11,]  .            .        .        0.71320  .      
## [12,]  .            .        .        .        0.35660
round(kronecker(R_time, R_area_scaled), 5)
## 12 x 12 sparse Matrix of class "dgTMatrix"
##                                                                              
##  [1,]  0.71319 -0.35659 -0.35659  .       -0.71319  0.35659  0.35659  .      
##  [2,] -0.35659  1.06978 -0.35659 -0.35659  0.35659 -1.06978  0.35659  0.35659
##  [3,] -0.35659 -0.35659  0.71319  .        0.35659  0.35659 -0.71319  .      
##  [4,]  .       -0.35659  .        0.35659  .        0.35659  .       -0.35659
##  [5,] -0.71319  0.35659  0.35659  .        1.42637 -0.71319 -0.71319  .      
##  [6,]  0.35659 -1.06978  0.35659  0.35659 -0.71319  2.13956 -0.71319 -0.71319
##  [7,]  0.35659  0.35659 -0.71319  .       -0.71319 -0.71319  1.42637  .      
##  [8,]  .        0.35659  .       -0.35659  .       -0.71319  .        0.71319
##  [9,]  .        .        .        .       -0.71319  0.35659  0.35659  .      
## [10,]  .        .        .        .        0.35659 -1.06978  0.35659  0.35659
## [11,]  .        .        .        .        0.35659  0.35659 -0.71319  .      
## [12,]  .        .        .        .        .        0.35659  .       -0.35659
##                                          
##  [1,]  .        .        .        .      
##  [2,]  .        .        .        .      
##  [3,]  .        .        .        .      
##  [4,]  .        .        .        .      
##  [5,] -0.71319  0.35659  0.35659  .      
##  [6,]  0.35659 -1.06978  0.35659  0.35659
##  [7,]  0.35659  0.35659 -0.71319  .      
##  [8,]  .        0.35659  .       -0.35659
##  [9,]  0.71319 -0.35659 -0.35659  .      
## [10,] -0.35659  1.06978 -0.35659 -0.35659
## [11,] -0.35659 -0.35659  0.71319  .      
## [12,]  .       -0.35659  .        0.35659

Confirm we can get the same thing by manually specifying Cmatrix.

fit <- inla(y ~ 1 +
              f(area, model = "besag", graph = adj, hyper = hyper_area,
                scale.model = TRUE,
                group = time,
                control.group = list(model = "rw1", scale.model = FALSE)),
            data = data, family = "poisson",
            control.inla = list(strategy = "gaussian", int.strategy = "eb"),
            control.compute = list(config = TRUE))


Q <- kronecker(R_time, R_area_scaled)
data$area.time <- 1:12

A <- rbind(rep(c(1, 0), c(4, 8)),
           rep(c(0, 1, 0), c(4, 4, 4)),
           rep(c(0, 1), c(8, 4)))
e <- c(0, 0, 0)


diagval <- INLA:::inla.set.f.default()$diagonal

fitQ <- inla(y ~ 1 +
               f(area.time, model = "generic0", Cmatrix = Q, hyper = hyper_area,
                 diagonal = diagval, extraconstr = list(A = A, e = e)),
             data = data, family = "poisson",
             control.inla = list(strategy = "gaussian", int.strategy = "eb"),
             control.compute = list(config = TRUE))

The effective parameters are equal:

fit$neffp
##                                       [,1]
## Expectected  number of parameters 7.576113
## Stdev of the number of parameters 0.000000
## Number of equivalent replicates   1.583926
fitQ$neffp
##                                       [,1]
## Expectected  number of parameters 7.576115
## Stdev of the number of parameters 0.000000
## Number of equivalent replicates   1.583925

Fixed effect and random effects are equal:

fit$summary.fixed[ , 1:2]
##                  mean        sd
## (Intercept) 0.8251967 0.1972447
fitQ$summary.fixed[ , 1:2]
##                  mean        sd
## (Intercept) 0.8251967 0.1972447
fit$summary.random[[1]][ , 1:3]
##    ID        mean        sd
## 1   1 -0.94219909 0.6243636
## 2   2 -0.00137828 0.4842483
## 3   3  0.24255352 0.4697330
## 4   4  0.70102386 0.4345768
## 5   1 -0.75627048 0.5427202
## 6   2  0.25072429 0.4145179
## 7   3  0.44648383 0.4143926
## 8   4  0.05906236 0.4967552
## 9   1  0.28635644 0.4684865
## 10  2 -0.24403882 0.5025126
## 11  3  0.10077233 0.4900527
## 12  4 -0.14308995 0.5497946
fitQ$summary.random[[1]][ , 1:3]
##    ID        mean        sd
## 1   1 -0.94219909 0.6243636
## 2   2 -0.00137828 0.4842483
## 3   3  0.24255352 0.4697330
## 4   4  0.70102386 0.4345768
## 5   5 -0.75627048 0.5427202
## 6   6  0.25072429 0.4145179
## 7   7  0.44648383 0.4143926
## 8   8  0.05906236 0.4967552
## 9   9  0.28635644 0.4684865
## 10 10 -0.24403882 0.5025126
## 11 11  0.10077233 0.4900527
## 12 12 -0.14308995 0.5497947

Marginal likelihood slightly different. Suspect difference in scaling constants?

fit$mlik
##                                            [,1]
## log marginal-likelihood (integration) -23.10433
## log marginal-likelihood (Gaussian)    -23.10433
fitQ$mlik
##                                            [,1]
## log marginal-likelihood (integration) -25.86114
## log marginal-likelihood (Gaussian)    -25.86114

The AR1 model

Basic AR1 model

  • The raw AR1 structure matrix is scaled by 1/(1 − ρ2) such that the marginal variance of the precision matrix is the specified precision.
rho <- 0.8

The AR1 precision matrix is given by

R_ar1 <- sparseMatrix(1:5, 1:5, x = 1)
diag(R_ar1)[2:4] <- 1 + rho^2
R_ar1[cbind(1:4, 2:5)] <- -rho
R_ar1[cbind(2:5, 1:4)] <- -rho

For a marginal precision of 1.0, scale the raw structure matrix:

Q_ar1 <- R_ar1 * 1 / (1 - rho^2)

Show this matches the Q matrix constructed by INLA

hyper_ar1 <- list(prec = list(initial = log(1), fixed = TRUE),
                  rho = list(initial = log((1 + rho) / (1 - rho)), fixed = TRUE))
                  
fit <- inla(y ~ 0 +
              f(time, model = "ar1", values = 1:5, hyper = hyper_ar1),
            data = datanull[1, ], family = "poisson",
            control.inla = list(strategy = "gaussian", int.strategy = "eb"),
            control.fixed = list(mean.intercept = 0, prec.intercept = 1),
            control.compute = list(config = TRUE))

fit$misc$configs$config[[1]]$Q[-1, -1]
## 5 x 5 sparse Matrix of class "dgCMatrix"
##                                                      
## [1,] 162757.6 -2.222223  .         .         .       
## [2,]      .    4.555557 -2.222223  .         .       
## [3,]      .    .         4.555557 -2.222223  .       
## [4,]      .    .         .         4.555557 -2.222223
## [5,]      .    .         .         .         2.777779
Q_ar1
## 5 x 5 sparse Matrix of class "dgCMatrix"
##                                                       
## [1,]  2.777778 -2.222222  .         .         .       
## [2,] -2.222222  4.555556 -2.222222  .         .       
## [3,]  .        -2.222222  4.555556 -2.222222  .       
## [4,]  .         .        -2.222222  4.555556 -2.222222
## [5,]  .         .         .        -2.222222  2.777778

ICAR x AR1 interaction

Three time points

R_ar1 <- sparseMatrix(1:3, 1:3, x = 1)
diag(R_ar1)[2] <- 1 + rho^2
R_ar1[cbind(1:2, 2:3)] <- -rho
R_ar1[cbind(2:3, 1:2)] <- -rho

Q_ar1 <- R_ar1 * 1 / (1 - rho^2)

fit <- inla(y ~ 0 +
              f(area, model = "besag", graph = adj, hyper = hyper_area,
                group = time,
                control.group = list(model = "ar1", hyper = hyper_ar1["rho"])),
            data = datanull, family = "poisson",
            control.inla = list(strategy = "gaussian", int.strategy = "eb"),
            control.fixed = list(mean.intercept = 0, prec.intercept = 1),
            control.compute = list(config = TRUE))

round(fit$misc$configs$config[[1]]$Q[-(1:3), -(1:3)], 5)
## 12 x 12 sparse Matrix of class "dgCMatrix"
##                                                                         
##  [1,] 162760.3 -2.77778 -2.77778  .           -4.44445  2.22222  2.22222
##  [2,]      .    8.33335 -2.77778 -2.77778      2.22222 -6.66667  2.22222
##  [3,]      .    .        5.55557  .            2.22222  2.22222 -4.44445
##  [4,]      .    .        .        2.77779      .        2.22222  .      
##  [5,]      .    .        .        .       162763.90254 -4.55556 -4.55556
##  [6,]      .    .        .        .            .       13.66668 -4.55556
##  [7,]      .    .        .        .            .        .        9.11113
##  [8,]      .    .        .        .            .        .        .      
##  [9,]      .    .        .        .            .        .        .      
## [10,]      .    .        .        .            .        .        .      
## [11,]      .    .        .        .            .        .        .      
## [12,]      .    .        .        .            .        .        .      
##                                                       
##  [1,]  .            .        .        .        .      
##  [2,]  2.22222      .        .        .        .      
##  [3,]  .            .        .        .        .      
##  [4,] -2.22222      .        .        .        .      
##  [5,]  .           -4.44445  2.22222  2.22222  .      
##  [6,] -4.55556      2.22222 -6.66667  2.22222  2.22222
##  [7,]  .            2.22222  2.22222 -4.44445  .      
##  [8,]  4.55557      .        2.22222  .       -2.22222
##  [9,]  .       162760.34699 -2.77778 -2.77778  .      
## [10,]  .            .        8.33335 -2.77778 -2.77778
## [11,]  .            .        .        5.55557  .      
## [12,]  .            .        .        .        2.77779
kronecker(Q_ar1, R_area)
## 12 x 12 sparse Matrix of class "dgTMatrix"
##                                                                            
##  [1,]  5.555556 -2.777778 -2.777778  .        -4.444444  2.222222  2.222222
##  [2,] -2.777778  8.333333 -2.777778 -2.777778  2.222222 -6.666667  2.222222
##  [3,] -2.777778 -2.777778  5.555556  .         2.222222  2.222222 -4.444444
##  [4,]  .        -2.777778  .         2.777778  .         2.222222  .       
##  [5,] -4.444444  2.222222  2.222222  .         9.111111 -4.555556 -4.555556
##  [6,]  2.222222 -6.666667  2.222222  2.222222 -4.555556 13.666667 -4.555556
##  [7,]  2.222222  2.222222 -4.444444  .        -4.555556 -4.555556  9.111111
##  [8,]  .         2.222222  .        -2.222222  .        -4.555556  .       
##  [9,]  .         .         .         .        -4.444444  2.222222  2.222222
## [10,]  .         .         .         .         2.222222 -6.666667  2.222222
## [11,]  .         .         .         .         2.222222  2.222222 -4.444444
## [12,]  .         .         .         .         .         2.222222  .       
##                                                        
##  [1,]  .         .         .         .         .       
##  [2,]  2.222222  .         .         .         .       
##  [3,]  .         .         .         .         .       
##  [4,] -2.222222  .         .         .         .       
##  [5,]  .        -4.444444  2.222222  2.222222  .       
##  [6,] -4.555556  2.222222 -6.666667  2.222222  2.222222
##  [7,]  .         2.222222  2.222222 -4.444444  .       
##  [8,]  4.555556  .         2.222222  .        -2.222222
##  [9,]  .         5.555556 -2.777778 -2.777778  .       
## [10,]  2.222222 -2.777778  8.333333 -2.777778 -2.777778
## [11,]  .        -2.777778 -2.777778  5.555556  .       
## [12,] -2.222222  .        -2.777778  .         2.777778

ICAR x AR1 interaction with scale.model = TRUE

fit <- inla(y ~ 0 +
              f(area, model = "besag", graph = adj, hyper = hyper_area,
                scale.model = TRUE,
                group = time, 
                control.group = list(model = "ar1", hyper = hyper_ar1["rho"])),
            data = datanull, family = "poisson",
            control.inla = list(strategy = "gaussian", int.strategy = "eb"),
            control.fixed = list(mean.intercept = 0, prec.intercept = 1),
            control.compute = list(config = TRUE))

round(fit$misc$configs$config[[1]]$Q[-(1:3), -(1:3)], 5)
## 12 x 12 sparse Matrix of class "dgCMatrix"
##                                                                         
##  [1,] 162756.8 -0.99054 -0.99054  .           -1.58486  0.79243  0.79243
##  [2,]      .    2.97162 -0.99054 -0.99054      0.79243 -2.37729  0.79243
##  [3,]      .    .        1.98108  .            0.79243  0.79243 -1.58486
##  [4,]      .    .        .        0.99055      .        0.79243  .      
##  [5,]      .    .        .        .       162758.04039 -1.62448 -1.62448
##  [6,]      .    .        .        .            .        4.87344 -1.62448
##  [7,]      .    .        .        .            .        .        3.24897
##  [8,]      .    .        .        .            .        .        .      
##  [9,]      .    .        .        .            .        .        .      
## [10,]      .    .        .        .            .        .        .      
## [11,]      .    .        .        .            .        .        .      
## [12,]      .    .        .        .            .        .        .      
##                                                       
##  [1,]  .            .        .        .        .      
##  [2,]  0.79243      .        .        .        .      
##  [3,]  .            .        .        .        .      
##  [4,] -0.79243      .        .        .        .      
##  [5,]  .           -1.58486  0.79243  0.79243  .      
##  [6,] -1.62448      0.79243 -2.37729  0.79243  0.79243
##  [7,]  .            0.79243  0.79243 -1.58486  .      
##  [8,]  1.62449      .        0.79243  .       -0.79243
##  [9,]  .       162756.77250 -0.99054 -0.99054  .      
## [10,]  .            .        2.97162 -0.99054 -0.99054
## [11,]  .            .        .        1.98108  .      
## [12,]  .            .        .        .        0.99055
round(kronecker(Q_ar1, R_area_scaled), 5)
## 12 x 12 sparse Matrix of class "dgTMatrix"
##                                                                              
##  [1,]  1.98107 -0.99053 -0.99053  .       -1.58486  0.79243  0.79243  .      
##  [2,] -0.99053  2.97160 -0.99053 -0.99053  0.79243 -2.37728  0.79243  0.79243
##  [3,] -0.99053 -0.99053  1.98107  .        0.79243  0.79243 -1.58486  .      
##  [4,]  .       -0.99053  .        0.99053  .        0.79243  .       -0.79243
##  [5,] -1.58486  0.79243  0.79243  .        3.24895 -1.62448 -1.62448  .      
##  [6,]  0.79243 -2.37728  0.79243  0.79243 -1.62448  4.87343 -1.62448 -1.62448
##  [7,]  0.79243  0.79243 -1.58486  .       -1.62448 -1.62448  3.24895  .      
##  [8,]  .        0.79243  .       -0.79243  .       -1.62448  .        1.62448
##  [9,]  .        .        .        .       -1.58486  0.79243  0.79243  .      
## [10,]  .        .        .        .        0.79243 -2.37728  0.79243  0.79243
## [11,]  .        .        .        .        0.79243  0.79243 -1.58486  .      
## [12,]  .        .        .        .        .        0.79243  .       -0.79243
##                                          
##  [1,]  .        .        .        .      
##  [2,]  .        .        .        .      
##  [3,]  .        .        .        .      
##  [4,]  .        .        .        .      
##  [5,] -1.58486  0.79243  0.79243  .      
##  [6,]  0.79243 -2.37728  0.79243  0.79243
##  [7,]  0.79243  0.79243 -1.58486  .      
##  [8,]  .        0.79243  .       -0.79243
##  [9,]  1.98107 -0.99053 -0.99053  .      
## [10,] -0.99053  2.97160 -0.99053 -0.99053
## [11,] -0.99053 -0.99053  1.98107  .      
## [12,]  .       -0.99053  .        0.99053

sessionInfo()

sessionInfo()
## R version 3.6.3 (2020-02-29)
## Platform: x86_64-apple-darwin15.6.0 (64-bit)
## Running under: macOS Catalina 10.15.4
## 
## Matrix products: default
## BLAS:   /Library/Frameworks/R.framework/Versions/3.6/Resources/lib/libRblas.0.dylib
## LAPACK: /Library/Frameworks/R.framework/Versions/3.6/Resources/lib/libRlapack.dylib
## 
## locale:
## [1] en_GB.UTF-8/en_GB.UTF-8/en_GB.UTF-8/C/en_GB.UTF-8/en_GB.UTF-8
## 
## attached base packages:
## [1] parallel  stats     graphics  grDevices utils     datasets  methods  
## [8] base     
## 
## other attached packages:
## [1] INLA_20.06.15 foreach_1.5.0 sp_1.4-2      Matrix_1.2-18
## 
## loaded via a namespace (and not attached):
##  [1] codetools_0.2-16   lattice_0.20-41    digest_0.6.25      grid_3.6.3        
##  [5] MatrixModels_0.4-1 magrittr_1.5       evaluate_0.14      highr_0.8         
##  [9] rlang_0.4.6        stringi_1.4.6      rmarkdown_2.2      splines_3.6.3     
## [13] iterators_1.0.12   tools_3.6.3        stringr_1.4.0      xfun_0.14         
## [17] yaml_2.2.1         compiler_3.6.3     htmltools_0.5.0    knitr_1.28