robertness
6/23/2014 - 11:46 AM

Simulate states for all nodes in a Bayesian network. Here, nodes are binary, but with minor tweeks this is extended to multi-level discrete

Simulate states for all nodes in a Bayesian network. Here, nodes are binary, but with minor tweeks this is extended to multi-level discrete (multinomial) or Gaussian.

library(bnlearn)
getR <- function(k, S){
  #Simulate a valid covariance matrix from an inverse Wishart
  tryCatch(#This uses the inverse of a wishart sim, non-zero prob of compuational singlularity,
    riwish(k, S),
    error = function(e) getR(k, S)
  )
}

simData <- function(k, n, var.names){
  #Simulate multivariate binomial with covariance
  require(MCMCpack)
  
  S <- toeplitz((k:1))
  resample <- sample(k, k)
  S <- S[resample, resample]
  R <- getR(k, S)
  sim.data <- tryCatch(
    as.data.frame(matrix(rnorm(n * k), ncol = k) %*% chol(R) > 0),
    error = function(e) simData(k, n, var.names)
  )
  for(i in 1:ncol(sim.data)){
    sim.data[, i] <- as.factor(sim.data[, i])
    levels(sim.data[, i]) <- c("LOW", "HIGH")
  }  
  names(sim.data) <- var.names
  sim.data
}
k <- 5 # number of  nodes
#Simulate a network structure
node.names <- paste("S", 1:k, sep = "")
node.net <-  random.graph(nodes = node.names, method = "melancon", num = 1, burn.in = 10^5, every = 100)[[1]]

#Fit parameters to the network
base.data <- simData(k, 10, nodes(node.net))
sim.data <- rbn(node.net, 100, base.data)
fitted.net <- bn.fit(node.net, sim.data, method="bayes")

#Recursively simulate states for all nodes in the network
simState <- function(x, parent.states){
  #Simulate a state for a given node given the parents
  parent.states
  sample.probs <- eval(parse(text = paste(c("probs.list[[x]][,", paste(paste("'", parent.states, "'", sep = ""), collapse = ","), "]"), collapse = "")))
  sample(c("LOW", "HIGH"), 1, prob = sample.probs)
}
simLineage <- function(time.t.states, node){
  #Recursively simulate the states of each node.  Given a node, simulate a state given its parents.
  #If the parents do not yet have a state, recursively apply the function to each parent
  node.parents <- parents(node.net, node)
  not.yet.simmed <- node.parents[sapply(node.parents, function(p) is.na(time.t.states[p]))]
  if(length(not.yet.simmed) > 0){
    for(p in not.yet.simmed){
      time.t.states <- simLineage(time.t.states, p)
    }
  }
  
  parent.states <- time.t.states[node.parents]
  time.t.states[node] <- simState(node, parent.states)
  time.t.states
}


#Make list of roots (nodes with no parents) and youngest (nodes with no children)
parents.list <- sapply(nodes(node.net), function(node) parents(node.net, node))
roots <- nodes(node.net)[sapply(parents.list, function(item) length(item) == 0)]
children.list <- sapply(nodes(node.net), function(node) children(node.net, node))
youngest <- nodes(node.net)[sapply(children.list, function(item) length(item) == 0) ]
probs.list <- lapply(nodes(node.net), function(node) fitted.net[[node]]$prob)
names(probs.list) <- nodes(node.net)
time.t.states <- rep(NA, nnodes(node.net))
names(time.t.states) <- nodes(node.net)

#Roots will be the base case in the recursion.  Simulate their states first.
time.t.states[roots] <- sapply(probs.list[roots], function(prob.vals) sample(c("LOW", "HIGH"), 1, prob=prob.vals))

#Starting with the youngest, apply the recursion such that it propagates back through the network until the roots are reached.
for(y in youngest){
  time.t.states <- simLineage(time.t.states, y)
}
time.t.states