Here is a little Greek-gods-and-weather DAG written up in R, with an example of how to take “forward samples” from the full joint distribution on its variables.
flip = function(p) runif(1) < p
rain.sim = function(i) {
flip = function(p) runif(1) < p
Zeus.angry = flip(.3)
Zeus.crying = flip(.2)
thunder = (Zeus.angry && flip(.7)) || flip(.3)
rain = (Zeus.angry && flip(.8)) || (Zeus.crying && flip(.99))
forgetful.gardener = flip(.4)
sprinkler = forgetful.gardener && flip(.7)
wet.grass = (rain && flip(.8)) || (sprinkler && flip(.9)) || flip(.1)
return(c(Zeus.angry = Zeus.angry, Zeus.crying = Zeus.crying, thunder = thunder, rain=rain,
forgetful.gardener = forgetful.gardener, sprinkler = sprinkler, wet.grass = wet.grass))
}
sapply(1:10, FUN=rain.sim)
## [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
## Zeus.angry FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE TRUE
## Zeus.crying TRUE FALSE TRUE FALSE FALSE TRUE TRUE FALSE TRUE
## thunder TRUE TRUE TRUE FALSE TRUE FALSE FALSE FALSE TRUE
## rain TRUE FALSE TRUE FALSE TRUE TRUE TRUE FALSE TRUE
## forgetful.gardener TRUE FALSE TRUE TRUE TRUE FALSE TRUE TRUE TRUE
## sprinkler TRUE FALSE TRUE TRUE TRUE FALSE TRUE FALSE FALSE
## wet.grass FALSE FALSE TRUE TRUE TRUE TRUE TRUE FALSE TRUE
## [,10]
## Zeus.angry FALSE
## Zeus.crying FALSE
## thunder FALSE
## rain FALSE
## forgetful.gardener TRUE
## sprinkler TRUE
## wet.grass FALSE
thunder and wet.grass? If you hear thunder while in bed at night, have you learned something about how likely it is that the grass will be wet in the morning? Why or why not?Here’s an example of how to estimate probabilities from this DAG using Monte Carlo sampling:
query = function(variable.name, n.sims = 500) {
sims = sapply(1:n.sims, FUN=rain.sim)
proportion = length(which(sims[variable.name,] == TRUE))/n.sims
return(proportion)
}
query('Zeus.angry')
## [1] 0.29
query('thunder')
## [1] 0.408
query() function to find out its approximate unconditional probability. (Or, more precisely, its approximate probability conditional on this model.) Make sure that you surround the variable name in quotes (single or double), both here and when working with the conditional.query() function below.Most often, what we’re doing with a DAG is observing some ‘downstream’ variables (effects) and trying to infer the values of some ‘upstream’ variables (causes). This means that the unconditional probability is not really what’s of interest, but rather the conditional probability given our observations. Here’s an example of how to write a conditional query function that will estimate conditional probability using rejection sampling - a simple and reliable, but frequently inefficient, method of Monte Carlo approximation.
conditional.query = function(query.variable, conditioners, n.sims=1000) {
samples = c()
while (length(samples) < n.sims) {
Zeus.angry = flip(.3)
Zeus.crying = flip(.2)
thunder = (Zeus.angry && flip(.7)) || flip(.3)
rain = (Zeus.angry && flip(.8)) || (Zeus.crying && flip(.99))
forgetful.gardener = flip(.4)
sprinkler = forgetful.gardener && flip(.7)
wet.grass = (rain && flip(.8)) || (sprinkler && flip(.9)) || flip(.1)
truth.values = c(Zeus.angry = Zeus.angry, Zeus.crying = Zeus.crying, thunder = thunder, rain=rain,
forgetful = forgetful.gardener, sprinkler = sprinkler, wet.grass = wet.grass)
query.index = which(query.variable == names(truth.values))
conditioner.indices = which(names(truth.values) %in% conditioners)
if (all(truth.values[conditioner.indices] == T)) {
samples = c(samples, truth.values[query.index])
}
# accept sample only if all conditioning statements are true
# repeat until you have n.sims-many samples
}
return(length(which(samples == T)) / n.sims) # proportion 'true' among accepted samples
}
What is the estimated \(P(\mathbf{wet\ grass})\)? What about \(P(\mathbf{wet\ grass}|\mathbf{thunder})\)?, \(P(\mathbf{wet\ grass}|\mathbf{thunder},\mathbf{Zeus\ angry})\)? \(P(\mathbf{wet\ grass}|\mathbf{Zeus\ angry})\)? Explain, in terms of the conditional independence relationships encoded in the DAG you drew above, why the approximate probabilities are distributed in this way.
conditional.query() affected as you increase the number of items in the conditioners vector that you feed to this function? Using a for-loop (or something fancier if you want), test this question systematically for conditioners vectors with lengths from 0 to 7. Use system.time() to time the results.Explain, in terms of (a) the unconditional probability of the conditioning statements and (b) the implementation of conditional.query() using a while-loop, why the result in (1f) obtains.
[Levy, exercise 3.1: Simpler formula for variance.] Show from the definition of the variance as \(\mathbf{Var}(X) ≡ \mathbb{E}[(X − \mathbf{E}(X))^2]\) that it can equivalently be written as \(\mathbf{Var}(X) = \mathbb{E}[X^2] − E[X]^2\), which we stated without proof in Section 2.9.2.
[Levy, Exercise 3.2: Covariance of conditionally independent random variables.] Use linearity of the expectation [section 3.3.1] to prove that if two discrete random variables \(X\) and \(Y\) are conditionally independent given your state of knowledge, then \(\mathbf{Cov}(X, Y) = 0\). (Hint: you can rewrite \(P(X=x,Y=y) = \sum_{x,y} [x \times p(X = x) \times y \times p(Y = y)]\) as \([\sum_{x} x \times p(X = x)] \times [\sum_y y \times p(Y = y)]\), since \(X\) and \(p(X = x)\) are constant with respect to \(y\), and vice versa.)