nutterb / dbn

Simulation of Discrete-Time Dynamic Bayesian Networks
0 stars 0 forks source link

plot.dbn #7

Closed nutterb closed 7 years ago

nutterb commented 7 years ago
jarrod-dalton commented 7 years ago

I like the "compact" representation (for lack of a better term) of the dbn. This is what plot.dbn() currently produces. Each variable is represented by just 1 node in the network, regardless of whether it's dynamic or static/global. For example, if you have a network with two dynamic variables, "a" and "b", and the conditionality is of the form a[t] | b[t-1] and b[t] | a[t-1], the plot() method would produce a two-node network with a link from 'a' to 'b' and another link back from 'b' to 'a'.

We can use different shapes/colors/etc. to represent global vs. static nodes, decision nodes, utility nodes, deterministic nodes, etc.

This is more visually compact and has the added benefit of being very compatible with the work you did for plot.HydeNetwork().

nutterb commented 7 years ago

For example, if you have a network with two dynamic variables, "a" and "b", and the conditionality is of the form a[t] | b[t-1] and b[t] | a[t-1], the plot() method would produce a two-node network with a link from 'a' to 'b' and another link back from 'b' to 'a'.

This sounds to me like it should be represented by:

dbn_question

But right now, dbn has no way of defining such a network. That seems to violate the acyclical characteristic we've assumed in the past. In fact, that I am able to define a cyclical network (see below) seems like a bug.

image

Should this be disallowed, or do I need to rethink how we define dependencies in dbn to permit it. This would require additional syntax parsing so that we could use

dbn(~ a[t] | b[t-1] + 
           b[t] | a[t-1])

Under these conditions, we'd have to explicitly define the temporal dependencies, but it would free us from some of the assumptions I've made.

jarrod-dalton commented 7 years ago

We need to allow for this mutual dependency. The graph you drew with the 4 nodes is the correct depiction, and note that it is a DAG.

The key concept here is that we allow for feedback mechanisms (a <-> b) by propagating the network out over successive time steps (a1 -> b2; b1 -> a2), thus removing cycles from the graph.

I was simply mentioning that I liked the visualization of the 2nd graph (a <-> b), because it's much more compact than if you had 2*t nodes (where t is the number of time steps plotted), and it gets the same information across.

I'd probably suggest some darker fill color for the dynamic nodes, to give a visual contrast from the global nodes. Maybe even a slightly larger relative font size, although this can be tricky to accomplish with the graphviz APIs.

Shall we let shape indicate random variable/deterministic node/decision node/utility node, and fill color indicate global vs. dynamic node?

As for the dependencies, we might do well to simply operate on the whole dbn without giving much thought to subsetting/slicing. Below is a toy example, with one global node and three dynamic nodes:

sex ~ dbern(0.505)
age[t] = age[t-1] + 1
sbp ~ dnorm(0.2*sbp[t-1] + (0.2^2)*sbp[t-2] + (0.2^3)*sbp[t-3], sd = 5)
ldl ~ dnorm(120 + 0.01*ldl[t-1] + 0.02*sbp[t-1], sd = 3)

Simulated data from this model might look something like

> z <- list(sex=0, age=53:57, sbp=c(147,153,150,160,157)+rnorm(5), ldl=c(121,130,131,128,136)+rnorm(5))
> z
$sex
[1] 0

$age
[1] 53 54 55 56 57

$sbp
[1] 146.1570 153.2719 149.9620 161.5739 156.4341

$ldl
[1] 119.8396 129.9186 129.9757 128.6296 136.1359

It turns out that the model for sbp is an abused version of an exponential smoothing model. We may wish to eventually be compatible with many of the simpler classes of time series forecasting models that DBNs generalize into one over-arching structure (arima, exponential smoothing, vector autoregression, etc.).

As for simulating a dynamic node's future value, one thing we could do is delegate the work to the user, with an appropriate tool. Kind of like how boot.boot() asks for a dataset and a function, and systematically applies the inputted function to the inputted dataset in some fashion to produce output.

So the model specification for dynamic nodes would involve the user providing a function to be included in the dbn object and called by the simulate.dbn() function (or whatever we name it) function. Inputs to these functions would need to be checked against the dbn structure somehow to ensure that all nodes are properly specified within the model. We'd also probably need to introduce some sort of syntax that allows the user to reference past values of dynamic nodes in some sort of systematic fashion.

Let me know if you want to do a call to strategize about this further.

nutterb commented 7 years ago

I've finished reworking the initial structures and plot method to support the compact version (which is default, and a good thing, too. expanding the network can get pretty messy. Take a look:

devtools::install_github("nutterb/dbn", ref = "devel-alt")

library(dbn)

x <- 
  dbn(~ a[t] | b[t-1] * q + 
        b[t] | a[t-1] * q,
      max_t = 2)

plot(x)

plot(x,
     expand = TRUE)

rplot01 rplot

jarrod-dalton commented 7 years ago

Very slick. Nice work.