--- title: "Sampling from Gaussian CAR and ICAR Models" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Sampling from Gaussian CAR and ICAR Models} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include=FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", message = FALSE, warning = FALSE, fig.width = 6, fig.height = 4, dpi = 72, fig.retina = 1 ) ``` ## Purpose This vignette demonstrates how to sample Gaussian random effects defined on a graph using **trafficCAR**. The emphasis is on models specified through a **sparse precision matrix**: \[ x \sim \mathrm{N}(Q^{-1} b,\; Q^{-1}), \] where \(Q\) is a sparse precision matrix and \(b\) is an optional linear term. Both **proper CAR** and **intrinsic CAR (ICAR)** specifications are illustrated. ```{r} library(trafficCAR) library(Matrix) ``` ## CAR and ICAR precision matrices Let \(A\) be a symmetric adjacency matrix with zero diagonal, and let \(D = \mathrm{diag}(A\mathbf{1})\). A common CAR family is \[ Q = \tau (D - \rho A), \] with \(\tau > 0\). For a **proper CAR**, choose \(\rho\) so that \(D - \rho A\) is positive definite. For an **ICAR**, the conventional choice is \(\rho = 1\), which yields a singular precision matrix. The function `car_precision()` constructs \(Q\) from \(A\). ## A small graph example Construct a simple path graph with 3 nodes. ```{r} A_small <- matrix( c(0, 1, 0, 1, 0, 1, 0, 1, 0), nrow = 3, byrow = TRUE ) A_small ``` ### Proper CAR ```{r} Q_car <- car_precision(A_small, type = "proper", rho = 0.5, tau = 1) Q_car ``` ### ICAR ```{r} Q_icar <- car_precision(A_small, type = "icar", tau = 1) Q_icar ``` ## Sampling from a sparse precision matrix Sampling is performed by `rmvnorm_prec()`, which draws from \(\mathcal{N}(Q^{-1} b, Q^{-1})\) using sparse factorizations. A dense covariance matrix is not formed. ### Zero-mean sampling ```{r} x0 <- trafficCAR:::rmvnorm_prec(Q_car) x0 ``` ### Nonzero mean (linear term) ```{r} b <- c(1, 0, -1) x1 <- trafficCAR:::rmvnorm_prec(Q_car, b = b) x1 ``` ## Notes on ICAR sampling For ICAR models, \(Q\) is singular and the implied Gaussian distribution is improper. Samples are identifiable only up to an additive constant. A common post-processing step is to center a draw. ```{r} # ICAR precision matrices are singular, so direct Cholesky-based sampling may fail. # A practical approach for simulation is to add a small ridge and then center. # This yields an approximate draw on the ICAR subspace. n_icar <- nrow(Q_icar) eps <- 1e-6 Q_icar_ridge <- Q_icar + Matrix::Diagonal(n_icar, x = eps) x_icar <- trafficCAR:::rmvnorm_prec(Q_icar_ridge) x_icar_centered <- x_icar - mean(x_icar) x_icar_centered ``` ## A road-like graph example This section illustrates CAR/ICAR sampling on a graph resembling a small road network. If `sf` is available, we build an adjacency matrix from the bundled `roads_small` dataset. Otherwise, we fall back to a small synthetic graph. ```{r} A_road <- NULL has_sf <- requireNamespace("sf", quietly = TRUE) if (has_sf) { data("roads_small", package = "trafficCAR") roads <- roads_small segments <- roads_to_segments( roads, crs_m = 3857, split_at_intersections = TRUE ) if (nrow(segments) > 200) { segments <- segments[seq_len(200), ] } adjacency <- build_adjacency(segments, crs_m = 3857) # Drop isolated segments to keep CAR models well-defined. if (any(adjacency$isolates)) { segments <- segments[!adjacency$isolates, ] adjacency <- build_adjacency(segments, crs_m = 3857) } A_road <- adjacency$A } ``` ### Option 2: Synthetic road-like graph (fallback) The following graph mimics a small road network with a T-junction. ```{r} if (is.null(A_road)) { # nodes: 1--2--3 and 2--4 (T-junction at node 2) edges <- matrix(c( 1, 2, 2, 3, 2, 4 ), ncol = 2, byrow = TRUE) if (requireNamespace("igraph", quietly = TRUE)) { g <- igraph::graph_from_edgelist(edges, directed = FALSE) A_road <- igraph::as_adjacency_matrix(g, sparse = TRUE, attr = NULL) } else { # minimal fallback without igraph (dense, small only) n <- max(edges) A_road <- matrix(0, n, n) for (k in seq_len(nrow(edges))) { i <- edges[k, 1]; j <- edges[k, 2] A_road[i, j] <- 1 A_road[j, i] <- 1 } A_road <- Matrix(A_road, sparse = TRUE) } } c(n = nrow(A_road), nnz = Matrix::nnzero(A_road)) ``` ### CAR/ICAR sampling on the road graph ```{r} # proper CAR (users should choose rho consistent with their graph and model) Q_car_road <- car_precision(A_road, type = "proper", rho = 0.5, tau = 1) # ICAR Q_icar_road <- car_precision(A_road, type = "icar", tau = 1) x_car_road <- trafficCAR:::rmvnorm_prec(Q_car_road) n_road <- nrow(Q_icar_road) eps <- 1e-6 Q_icar_road_ridge <- Q_icar_road + Matrix::Diagonal(n_road, x = eps) x_icar_road <- trafficCAR:::rmvnorm_prec(Q_icar_road_ridge) x_icar_road <- x_icar_road - mean(x_icar_road) summary(x_car_road) summary(x_icar_road) ``` ## Summary - Proper CAR models yield positive definite precision matrices and support direct Gaussian sampling. - ICAR models yield singular precisions; sampled fields are defined up to an additive constant and are typically centered or constrained. - Sampling from sparse precision matrices avoids dense covariance calculations and scales well to larger graphs.