Below is the hands-on exercises on the advanced methods. This will cover the heuristic search and how to perform Bayesian model averaging.
Note: in the following text: Bayesian network, structure and DAG are synonyms.
Heuristic search
The mostProbable()
function is limited to 20 to 25 nodes (if computed on a computing cluster even with a limited number of parent). Then for larger problem this approach becomes intractable form a computational point of view. The main advantages of the mostProbable()
function is that the returned structure is the one that has the maximal possible score, thus it is called exact in that sense (Koivisto & Sood, 2004). It compares all possible structure to select the optimal one.
The heuristic searches are techniques that tends to perform a greedy optimization. Then there is no guarantees to reach the maximum score. In this context greedy means: perform local optimization and hoping to get the overall maximum.
We will use a Hill-Climber (Korb & Nicholson, 2010). We need to set up the number of searches and the number of steps per searches. The starting point (here we choose a random Directed Acyclic Graph (DAG)). The rest of parameters is equivalent to the ones used in buildscorecache()
function.
abndata <- adg %>%
dplyr::select(-farm) %>%
as.data.frame()
abndata[,1:5] <- as.data.frame(lapply(abndata[,1:5], factor))
dist <- list(AR = "binomial", pneumS = "binomial", female="binomial",
livdam= "binomial", eggs = "binomial",wormCount = "poisson",
age= "gaussian", adg = "gaussian")
#set maximum number of possible parent per node
max.par <- 4
# compute a cache of scores
mycache <- buildScoreCache(data.df = (abndata),
data.dists = dist,
max.parents = max.par)
# set number of searches and number of steps
num.searches <- 200
max.steps <- 150
# Hill-climber
heur.res <- quiet(searchHeuristic(score.cache = mycache,
score = "mlik",
data.dists = dist,
max.parents = 4,
start.dag = "random",
num.searches = num.searches,
max.steps = max.steps,
seed = 3213,
verbose = TRUE,
algo = "hc"))
# for comparison let us compute the maximum exact score
mydag <- (mostProbable(score.cache = mycache))
Step1. completed max alpha_i(S) for all i and S
Total sets g(S) to be evaluated over: 256
# alternatively you could use the precomputed "mydag" using:
## load(mydag.Rdata)
fabn <- fitAbn(object = mydag)
# plot Hill-Climber scores
df.heur <- unlist(heur.res$scores)
plot(NULL,lty=1, xlab="Index of heuristic search",ylab="BN score", ylim = c(min(unlist(df.heur)),max(unlist(df.heur))), xlim = c(0,num.searches))
for(i in 1:num.searches){
if(sum(i==order(df.heur,decreasing = TRUE)[1:10])){
points(x=i,y=df.heur[i],type="p",pch=19, col=rgb(0,0,1, 0.8),lwd = 2)
}else{
points(x=i,y=df.heur[i],type="p",pch=19, col=rgb(0,0,0, 0.3))
}
}
points(x = max(unlist(df.heur)),y = max(unlist(df.heur)),col="red",pch=19)
abline(h = fabn$mlik, col="red",lty = 3)
Above we plot the maximum scores after 150 steps out of 200 different searches of a Hill-Climber. The blues dotes are the 10 best searches. The red dashed line is the maximum possible score.
# let us plot the evolution of scores during optimization
Long <- (heur.res$detailed.score)
Long.arr <- array(unlist(Long), dim = c(nrow(Long[[1]]), ncol(Long[[1]]), length(Long)))
plot(NULL,lty=1, xlab="Number of Steps",ylab="BN score", ylim = c(min(unlist(df.heur)),max(unlist(df.heur))), xlim = c(0,max.steps))
for(i in 1:num.searches){
if(sum(i==order(unlist(heur.res$scores),decreasing = TRUE)[1:10])){
points(x=1:(max.steps-1),y=Long.arr[1,,i],type="l",lty=1, col=rgb(0,0,1, 0.8),lwd = 2)
}else{
points(x=1:(max.steps-1),y=Long.arr[1,,i],type="l",lty=1, col=rgb(0,0,0, 0.25))
}
}
lines(x=1:(max.steps-1),y=Long.arr[1,,which.max(unlist(heur.res$scores))],type="l",col="red",lwd=3)
abline(h = fabn$mlik, col="red",lty = 3)
Above we plot the scores in function of the steps for the 200 searches. The blue lines are the 10 best and the red one is the one that has the highest score. As one can see, in searches that work well the optimization is already good after 60 steps. This plot is a diagnostic plot used to set up the number of required steps.
MCMC over the structures
Usually, the output of a Bayesian network analysis of a dataset ends-up with a single well adjusted DAG. From the researcher point of view this could be frustrating. Indeed, the model is the one that is best supported by the data but the uncertainty quantification is missing. Classically in epidemiology, researchers are used to express point estimate with an uncertainty measure. An arc in a Bayesian network is a point estimate, we will see how to perform model averaging. The link strength measure is designed to account for that. An more natural alternative is to perform MCMC over structures (Friedman & Koller, 2003).
We use the mcmcabn()
function on the cache of pre-computed networks scores. One needs to define the type of score used (here is the marginal likelihood mlik
). The maximum of number of parents per node (same as the one used in buildscorecache()
). The MCMC learning scheme, defined as: number of MCMC samples, number of thinned sampled (to avoid autocorrelation) and the length of the burn-in phase. Possibly a starting DAG and a structural prior. We also need to select the relative probability of performing radical moves (shuffling). Indeed, a naive MCMC approach is known to get very easily stuck in local maximum (for more details see: (Grzegorczyk & Husmeier, 2008; Su & Borsuk, 2016)).
mcmc.out <- mcmcabn(score.cache = mycache,
score = "mlik",
data.dists = dist,
max.parents = 4,
mcmc.scheme = c(1000,9,500),
seed = 321,
verbose = FALSE,
start.dag = "random",
prob.rev = 0.07,
prob.mbr = 0.07,
prior.choice = 1)
This is again computationally complex!
Her is a plot of the MCMC samples. One can see the scores of the structures on the y-axis in function of the index steps. The dots are the radical moves. One the right side a histogram shows the number of structures with a given score. As one can see the histogram is very peaked on the maximum possible score.
One can also display the cumulative maximum score (used for network score optimization).
But the major advantage of this method is the possibility of querying the MCMC sample using a formula statement.
# average individual arc support
query(mcmcabn = mcmc.out)
AR pneumS female livdam eggs wormCount
AR 0.00000000 0.01069893 0.01139886 0.01159884 0.07599240 0.00349965
pneumS 0.00589941 0.00000000 0.01539846 0.00829917 0.10398960 0.00429957
female 0.00579942 0.01879812 0.00000000 0.01769823 0.00659934 0.00249975
livdam 0.00689931 0.00999900 0.01119888 0.00000000 0.44275572 0.00439956
eggs 0.14908509 0.09239076 0.01019898 0.29377062 0.00000000 0.02959704
wormCount 0.76062394 0.08119188 0.03349665 0.07039296 0.91700830 0.00000000
age 0.13568643 0.07349265 0.15768423 0.11338866 0.09739026 0.01399860
adg 0.03019698 0.01589841 0.08359164 0.00959904 0.17288271 0.00349965
age adg
AR 0.77212279 0.08649135
pneumS 0.19238076 0.03989601
female 0.29857014 0.12168783
livdam 0.06649335 0.01169883
eggs 0.25057494 0.36466353
wormCount 0.88481152 0.31056894
age 0.00000000 0.47095290
adg 0.52864714 0.00000000
# probability that worm count being linked to age but not to female directly
query(mcmcabn = mcmc.out,formula = ~wormCount|age-wormCount|female)
[1] 0.01889811
# probability that worm count being directly linked to age and adg and that adg is link to age (undirected)
query(mcmcabn = mcmc.out,formula = ~wormCount|age + wormCount|adg + age|adg)+
query(mcmcabn = mcmc.out,formula = ~wormCount|age + wormCount|adg + adg|age)
[1] 0.2451755
References
Friedman, N., & Koller, D. (2003). Being bayesian about network structure. A bayesian approach to structure discovery in bayesian networks. Machine Learning, 50(1-2), 95–125.
Grzegorczyk, M., & Husmeier, D. (2008). Improving the structure MCMC sampler for bayesian networks by introducing a new edge reversal move. Machine Learning, 71(2-3), 265.
Koivisto, M., & Sood, K. (2004). Exact bayesian structure discovery in bayesian networks. Journal of Machine Learning Research, 5(May), 549–573.
Korb, K. B., & Nicholson, A. E. (2010). Bayesian artificial intelligence. CRC press.
Su, C., & Borsuk, M. E. (2016). Improving structure mcmc for bayesian networks through markov blanket resampling. The Journal of Machine Learning Research, 17(1), 4042–4061.
LS0tCnRpdGxlOiAiSGFuZHMtb24gZXhlcmNpc2U6IGFkdmFuY2VkIGZlYXR1cmVzIG9mIEFCTiBhbmFseXNpcyAtIFVzZVIhIENvbmZlcmVuY2UiCmZvbnRzaXplOiAxM3B0Cm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB0cnVlCiAgICB0b2NfZGVwdGg6IDIKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKYmlibGlvZ3JhcGh5OiBiaWJfYWR2YW5jZWQuYmliCmNzbDogYXBhLmNzbAotLS0KCiZuYnNwOwoKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IEZBTFNFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBjb2xsYXBzZT1GQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9MTAsIGNvbW1lbnQgPSBOQSkKCm9wdGlvbnMoc2NpcGVuPTk5OSkKCnF1aWV0IDwtIGZ1bmN0aW9uKHgpIHsgCiAgc2luayh0ZW1wZmlsZSgpKSAKICBvbi5leGl0KHNpbmsoKSkgCiAgaW52aXNpYmxlKGZvcmNlKHgpKSAKfSAKCiMjIGxpYnJhcnkKbGlicmFyeShrbml0cikKbGlicmFyeShrYWJsZUV4dHJhKQpsaWJyYXJ5KGFibikKbGlicmFyeShtY21jYWJuKQoKIyMgbG9hZCBkYXRhCgpkYXRhKCJhZGciLCBwYWNrYWdlID0gImFibiIpCgphZGcgPC0gYWRnICU+JSBhcy5kYXRhLmZyYW1lKCkKCmRyb3AgPC0gd2hpY2goY29sbmFtZXMoZHQpJWluJSBjKCJwbmV1bSIsICJlcGc1IiwgIndvcm1zIiwgImZhcm0iKSkKCmBgYAoKQmVsb3cgaXMgdGhlIGhhbmRzLW9uIGV4ZXJjaXNlcyBvbiB0aGUgYWR2YW5jZWQgbWV0aG9kcy4gVGhpcyB3aWxsIGNvdmVyIHRoZSAqKmhldXJpc3RpYyBzZWFyY2gqKiBhbmQgaG93IHRvIHBlcmZvcm0gKipCYXllc2lhbiBtb2RlbCBhdmVyYWdpbmcqKi4KCipOb3RlOiBpbiB0aGUgZm9sbG93aW5nIHRleHQ6IEJheWVzaWFuIG5ldHdvcmssIHN0cnVjdHVyZSBhbmQgREFHIGFyZSBzeW5vbnltcy4qCgoKIyBIZXVyaXN0aWMgc2VhcmNoCgpUaGUgYG1vc3RQcm9iYWJsZSgpYCBmdW5jdGlvbiBpcyBsaW1pdGVkIHRvIDIwIHRvIDI1IG5vZGVzIChpZiBjb21wdXRlZCBvbiBhIGNvbXB1dGluZyBjbHVzdGVyIGV2ZW4gd2l0aCBhIGxpbWl0ZWQgbnVtYmVyIG9mIHBhcmVudCkuIFRoZW4gZm9yIGxhcmdlciBwcm9ibGVtIHRoaXMgYXBwcm9hY2ggYmVjb21lcyBpbnRyYWN0YWJsZSBmb3JtIGEgY29tcHV0YXRpb25hbCBwb2ludCBvZiB2aWV3LiBUaGUgbWFpbiBhZHZhbnRhZ2VzIG9mIHRoZSBgbW9zdFByb2JhYmxlKClgIGZ1bmN0aW9uIGlzIHRoYXQgdGhlIHJldHVybmVkIHN0cnVjdHVyZSBpcyB0aGUgb25lIHRoYXQgaGFzIHRoZSBtYXhpbWFsIHBvc3NpYmxlIHNjb3JlLCB0aHVzIGl0IGlzIGNhbGxlZCAqKmV4YWN0KiogaW4gdGhhdCBzZW5zZSBbQGtvaXZpc3RvMjAwNGV4YWN0XS4gSXQgY29tcGFyZXMgYWxsIHBvc3NpYmxlIHN0cnVjdHVyZSB0byBzZWxlY3QgdGhlIG9wdGltYWwgb25lLiAKClRoZSBoZXVyaXN0aWMgc2VhcmNoZXMgYXJlIHRlY2huaXF1ZXMgdGhhdCB0ZW5kcyB0byBwZXJmb3JtIGEgZ3JlZWR5IG9wdGltaXphdGlvbi4gVGhlbiB0aGVyZSBpcyBubyBndWFyYW50ZWVzIHRvIHJlYWNoIHRoZSBtYXhpbXVtIHNjb3JlLiBJbiB0aGlzIGNvbnRleHQgZ3JlZWR5IG1lYW5zOiBwZXJmb3JtIGxvY2FsIG9wdGltaXphdGlvbiBhbmQgaG9waW5nIHRvIGdldCB0aGUgb3ZlcmFsbCBtYXhpbXVtLgoKV2Ugd2lsbCB1c2UgYSBIaWxsLUNsaW1iZXIgW0Brb3JiMjAxMGJheWVzaWFuXS4gV2UgbmVlZCB0byBzZXQgdXAgdGhlIG51bWJlciBvZiBzZWFyY2hlcyBhbmQgdGhlIG51bWJlciBvZiBzdGVwcyBwZXIgc2VhcmNoZXMuIFRoZSBzdGFydGluZyBwb2ludCAoaGVyZSB3ZSBjaG9vc2UgYSByYW5kb20gRGlyZWN0ZWQgQWN5Y2xpYyBHcmFwaCAoREFHKSkuIFRoZSByZXN0IG9mIHBhcmFtZXRlcnMgaXMgZXF1aXZhbGVudCB0byB0aGUgb25lcyB1c2VkIGluIGBidWlsZHNjb3JlY2FjaGUoKWAgZnVuY3Rpb24uCgpgYGB7ciBoZXVyaXN0aWMxLCBlY2hvPVRSVUUsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD00fQoKYWJuZGF0YSA8LSBhZGcgJT4lCiAgZHBseXI6OnNlbGVjdCgtZmFybSkgJT4lCiAgYXMuZGF0YS5mcmFtZSgpCgphYm5kYXRhWywxOjVdIDwtIGFzLmRhdGEuZnJhbWUobGFwcGx5KGFibmRhdGFbLDE6NV0sIGZhY3RvcikpCmRpc3QgPC0gbGlzdChBUiA9ICJiaW5vbWlhbCIsIHBuZXVtUyA9ICJiaW5vbWlhbCIsIGZlbWFsZT0iYmlub21pYWwiLCAKICAgICAgICAgICAgIGxpdmRhbT0gImJpbm9taWFsIiwgZWdncyA9ICJiaW5vbWlhbCIsd29ybUNvdW50ID0gInBvaXNzb24iLAogICAgICAgICAgICAgYWdlPSAiZ2F1c3NpYW4iLCBhZGcgPSAiZ2F1c3NpYW4iKQoKI3NldCBtYXhpbXVtIG51bWJlciBvZiBwb3NzaWJsZSBwYXJlbnQgcGVyIG5vZGUKbWF4LnBhciA8LSA0CgojIGNvbXB1dGUgYSBjYWNoZSBvZiBzY29yZXMKbXljYWNoZSA8LSBidWlsZFNjb3JlQ2FjaGUoZGF0YS5kZiA9IChhYm5kYXRhKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEuZGlzdHMgPSBkaXN0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4LnBhcmVudHMgPSBtYXgucGFyKQoKIyBzZXQgbnVtYmVyIG9mIHNlYXJjaGVzIGFuZCBudW1iZXIgb2Ygc3RlcHMKbnVtLnNlYXJjaGVzIDwtIDIwMAptYXguc3RlcHMgPC0gMTUwCgojIEhpbGwtY2xpbWJlcgpoZXVyLnJlcyA8LSBxdWlldChzZWFyY2hIZXVyaXN0aWMoc2NvcmUuY2FjaGUgPSBteWNhY2hlLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzY29yZSA9ICJtbGlrIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YS5kaXN0cyA9IGRpc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heC5wYXJlbnRzID0gNCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhcnQuZGFnID0gInJhbmRvbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG51bS5zZWFyY2hlcyA9IG51bS5zZWFyY2hlcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4LnN0ZXBzID0gbWF4LnN0ZXBzLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzZWVkID0gMzIxMywKICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGFsZ28gPSAiaGMiKSkKCiMgZm9yIGNvbXBhcmlzb24gbGV0IHVzIGNvbXB1dGUgdGhlIG1heGltdW0gZXhhY3Qgc2NvcmUKbXlkYWcgPC0gKG1vc3RQcm9iYWJsZShzY29yZS5jYWNoZSA9IG15Y2FjaGUpKQojIGFsdGVybmF0aXZlbHkgeW91IGNvdWxkIHVzZSB0aGUgcHJlY29tcHV0ZWQgIm15ZGFnIiB1c2luZzoKIyMgbG9hZChteWRhZy5SZGF0YSkgCgpmYWJuIDwtIGZpdEFibihvYmplY3QgPSBteWRhZykKCiMgcGxvdCBIaWxsLUNsaW1iZXIgc2NvcmVzCmRmLmhldXIgPC0gdW5saXN0KGhldXIucmVzJHNjb3JlcykKcGxvdChOVUxMLGx0eT0xLCB4bGFiPSJJbmRleCBvZiBoZXVyaXN0aWMgc2VhcmNoIix5bGFiPSJCTiBzY29yZSIsIHlsaW0gPSBjKG1pbih1bmxpc3QoZGYuaGV1cikpLG1heCh1bmxpc3QoZGYuaGV1cikpKSwgeGxpbSA9IGMoMCxudW0uc2VhcmNoZXMpKQpmb3IoaSBpbiAxOm51bS5zZWFyY2hlcyl7CiAgaWYoc3VtKGk9PW9yZGVyKGRmLmhldXIsZGVjcmVhc2luZyA9IFRSVUUpWzE6MTBdKSl7CiAgICBwb2ludHMoeD1pLHk9ZGYuaGV1cltpXSx0eXBlPSJwIixwY2g9MTksIGNvbD1yZ2IoMCwwLDEsIDAuOCksbHdkID0gMikKICAgIH1lbHNlewogICAgcG9pbnRzKHg9aSx5PWRmLmhldXJbaV0sdHlwZT0icCIscGNoPTE5LCBjb2w9cmdiKDAsMCwwLCAwLjMpKQogIH0KfQpwb2ludHMoeCA9IG1heCh1bmxpc3QoZGYuaGV1cikpLHkgPSBtYXgodW5saXN0KGRmLmhldXIpKSxjb2w9InJlZCIscGNoPTE5KQphYmxpbmUoaCA9IGZhYm4kbWxpaywgY29sPSJyZWQiLGx0eSA9IDMpCgpgYGAKCkFib3ZlIHdlIHBsb3QgdGhlIG1heGltdW0gc2NvcmVzIGFmdGVyIDE1MCBzdGVwcyBvdXQgb2YgMjAwIGRpZmZlcmVudCBzZWFyY2hlcyBvZiBhIEhpbGwtQ2xpbWJlci4gVGhlIGJsdWVzIGRvdGVzIGFyZSB0aGUgMTAgYmVzdCBzZWFyY2hlcy4gVGhlIHJlZCBkYXNoZWQgbGluZSBpcyB0aGUgbWF4aW11bSBwb3NzaWJsZSBzY29yZS4gCgpgYGB7ciBoZXVyaXN0aWMyLCBlY2hvPVRSVUUsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD00fQojIGxldCB1cyBwbG90IHRoZSBldm9sdXRpb24gb2Ygc2NvcmVzIGR1cmluZyBvcHRpbWl6YXRpb24KTG9uZyA8LSAoaGV1ci5yZXMkZGV0YWlsZWQuc2NvcmUpCkxvbmcuYXJyIDwtIGFycmF5KHVubGlzdChMb25nKSwgZGltID0gYyhucm93KExvbmdbWzFdXSksIG5jb2woTG9uZ1tbMV1dKSwgbGVuZ3RoKExvbmcpKSkKcGxvdChOVUxMLGx0eT0xLCB4bGFiPSJOdW1iZXIgb2YgU3RlcHMiLHlsYWI9IkJOIHNjb3JlIiwgeWxpbSA9IGMobWluKHVubGlzdChkZi5oZXVyKSksbWF4KHVubGlzdChkZi5oZXVyKSkpLCB4bGltID0gYygwLG1heC5zdGVwcykpCmZvcihpIGluIDE6bnVtLnNlYXJjaGVzKXsKICBpZihzdW0oaT09b3JkZXIodW5saXN0KGhldXIucmVzJHNjb3JlcyksZGVjcmVhc2luZyA9IFRSVUUpWzE6MTBdKSl7CiAgICBwb2ludHMoeD0xOihtYXguc3RlcHMtMSkseT1Mb25nLmFyclsxLCxpXSx0eXBlPSJsIixsdHk9MSwgY29sPXJnYigwLDAsMSwgMC44KSxsd2QgPSAyKQogICAgfWVsc2V7CiAgICBwb2ludHMoeD0xOihtYXguc3RlcHMtMSkseT1Mb25nLmFyclsxLCxpXSx0eXBlPSJsIixsdHk9MSwgY29sPXJnYigwLDAsMCwgMC4yNSkpCiAgfQp9CmxpbmVzKHg9MToobWF4LnN0ZXBzLTEpLHk9TG9uZy5hcnJbMSwsd2hpY2gubWF4KHVubGlzdChoZXVyLnJlcyRzY29yZXMpKV0sdHlwZT0ibCIsY29sPSJyZWQiLGx3ZD0zKQphYmxpbmUoaCA9IGZhYm4kbWxpaywgY29sPSJyZWQiLGx0eSA9IDMpCgpgYGAKQWJvdmUgd2UgcGxvdCB0aGUgc2NvcmVzIGluIGZ1bmN0aW9uIG9mIHRoZSBzdGVwcyBmb3IgdGhlIDIwMCBzZWFyY2hlcy4gVGhlIGJsdWUgbGluZXMgYXJlIHRoZSAxMCBiZXN0IGFuZCB0aGUgcmVkIG9uZSBpcyAqKnRoZSoqIG9uZSB0aGF0IGhhcyB0aGUgaGlnaGVzdCBzY29yZS4gQXMgb25lIGNhbiBzZWUsIGluIHNlYXJjaGVzIHRoYXQgd29yayB3ZWxsIHRoZSBvcHRpbWl6YXRpb24gaXMgYWxyZWFkeSBnb29kIGFmdGVyIDYwIHN0ZXBzLiBUaGlzIHBsb3QgaXMgYSBkaWFnbm9zdGljIHBsb3QgdXNlZCB0byBzZXQgdXAgdGhlIG51bWJlciBvZiByZXF1aXJlZCBzdGVwcy4gCgojIE1DTUMgb3ZlciB0aGUgc3RydWN0dXJlcwoKVXN1YWxseSwgdGhlIG91dHB1dCBvZiBhIEJheWVzaWFuIG5ldHdvcmsgYW5hbHlzaXMgb2YgYSBkYXRhc2V0IGVuZHMtdXAgd2l0aCBhIHNpbmdsZSB3ZWxsIGFkanVzdGVkIERBRy4gRnJvbSB0aGUgcmVzZWFyY2hlciBwb2ludCBvZiB2aWV3IHRoaXMgY291bGQgYmUgZnJ1c3RyYXRpbmcuIEluZGVlZCwgdGhlIG1vZGVsIGlzIHRoZSBvbmUgdGhhdCBpcyBiZXN0IHN1cHBvcnRlZCBieSB0aGUgZGF0YSBidXQgdGhlIHVuY2VydGFpbnR5IHF1YW50aWZpY2F0aW9uIGlzIG1pc3NpbmcuIENsYXNzaWNhbGx5IGluIGVwaWRlbWlvbG9neSwgcmVzZWFyY2hlcnMgYXJlIHVzZWQgdG8gZXhwcmVzcyBwb2ludCBlc3RpbWF0ZSB3aXRoIGFuIHVuY2VydGFpbnR5IG1lYXN1cmUuIEFuIGFyYyBpbiBhIEJheWVzaWFuIG5ldHdvcmsgaXMgYSBwb2ludCBlc3RpbWF0ZSwgd2Ugd2lsbCBzZWUgaG93IHRvIHBlcmZvcm0gbW9kZWwgYXZlcmFnaW5nLiBUaGUgKmxpbmsgc3RyZW5ndGgqIG1lYXN1cmUgaXMgZGVzaWduZWQgdG8gYWNjb3VudCBmb3IgdGhhdC4gQW4gbW9yZSBuYXR1cmFsIGFsdGVybmF0aXZlIGlzIHRvIHBlcmZvcm0gTUNNQyBvdmVyIHN0cnVjdHVyZXMgW0BmcmllZG1hbjIwMDNiZWluZ10uIAoKV2UgdXNlIHRoZSBgbWNtY2FibigpYCBmdW5jdGlvbiBvbiB0aGUgY2FjaGUgb2YgcHJlLWNvbXB1dGVkIG5ldHdvcmtzIHNjb3Jlcy4gT25lIG5lZWRzIHRvIGRlZmluZSB0aGUgdHlwZSBvZiBzY29yZSB1c2VkIChoZXJlIGlzIHRoZSBtYXJnaW5hbCBsaWtlbGlob29kIGBtbGlrYCkuIFRoZSBtYXhpbXVtIG9mIG51bWJlciBvZiBwYXJlbnRzIHBlciBub2RlIChzYW1lIGFzIHRoZSBvbmUgdXNlZCBpbiBgYnVpbGRzY29yZWNhY2hlKClgKS4gVGhlIE1DTUMgbGVhcm5pbmcgc2NoZW1lLCBkZWZpbmVkIGFzOiBudW1iZXIgb2YgTUNNQyBzYW1wbGVzLCBudW1iZXIgb2YgdGhpbm5lZCBzYW1wbGVkICh0byBhdm9pZCBhdXRvY29ycmVsYXRpb24pIGFuZCB0aGUgbGVuZ3RoIG9mIHRoZSBidXJuLWluIHBoYXNlLiBQb3NzaWJseSBhIHN0YXJ0aW5nIERBRyBhbmQgYSBzdHJ1Y3R1cmFsIHByaW9yLiBXZSBhbHNvIG5lZWQgdG8gc2VsZWN0IHRoZSByZWxhdGl2ZSBwcm9iYWJpbGl0eSBvZiBwZXJmb3JtaW5nIHJhZGljYWwgbW92ZXMgKHNodWZmbGluZykuIEluZGVlZCwgYSBuYWl2ZSBNQ01DIGFwcHJvYWNoIGlzIGtub3duIHRvIGdldCB2ZXJ5IGVhc2lseSBzdHVjayBpbiBsb2NhbCBtYXhpbXVtIChmb3IgbW9yZSBkZXRhaWxzIHNlZTogW0BncnplZ29yY3p5azIwMDhpbXByb3Zpbmc7QHN1MjAxNmltcHJvdmluZ10pLiAKCmBgYHtyLGV2YWw9RkFMU0UsIGVjaG89VFJVRX0KbWNtYy5vdXQgPC0gbWNtY2FibihzY29yZS5jYWNoZSA9IG15Y2FjaGUsCiAgICAgICAgICAgICAgICAgIHNjb3JlID0gIm1saWsiLAogICAgICAgICAgICAgICAgICBkYXRhLmRpc3RzID0gZGlzdCwKICAgICAgICAgICAgICAgICAgbWF4LnBhcmVudHMgPSA0LAogICAgICAgICAgICAgICAgICBtY21jLnNjaGVtZSA9IGMoMTAwMCw5LDUwMCksCiAgICAgICAgICAgICAgICAgIHNlZWQgPSAzMjEsCiAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgc3RhcnQuZGFnID0gInJhbmRvbSIsCiAgICAgICAgICAgICAgICAgIHByb2IucmV2ID0gMC4wNywKICAgICAgICAgICAgICAgICAgcHJvYi5tYnIgPSAwLjA3LAogICAgICAgICAgICAgICAgICBwcmlvci5jaG9pY2UgPSAxKQpgYGAKClRoaXMgaXMgYWdhaW4gY29tcHV0YXRpb25hbGx5IGNvbXBsZXghCgpIZXIgaXMgYSBwbG90IG9mIHRoZSBNQ01DIHNhbXBsZXMuIE9uZSBjYW4gc2VlIHRoZSBzY29yZXMgb2YgdGhlIHN0cnVjdHVyZXMgb24gdGhlIHktYXhpcyBpbiBmdW5jdGlvbiBvZiB0aGUgaW5kZXggc3RlcHMuIFRoZSBkb3RzIGFyZSB0aGUgcmFkaWNhbCBtb3Zlcy4gT25lIHRoZSByaWdodCBzaWRlIGEgaGlzdG9ncmFtIHNob3dzIHRoZSBudW1iZXIgb2Ygc3RydWN0dXJlcyB3aXRoIGEgZ2l2ZW4gc2NvcmUuIEFzIG9uZSBjYW4gc2VlIHRoZSBoaXN0b2dyYW0gaXMgdmVyeSBwZWFrZWQgb24gdGhlIG1heGltdW0gcG9zc2libGUgc2NvcmUuIAoKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD00fQpsb2FkKCJtY21jLlJkYXRhIikKcGxvdChtY21jLm91dCkKYGBgCgpPbmUgY2FuIGFsc28gZGlzcGxheSB0aGUgY3VtdWxhdGl2ZSBtYXhpbXVtIHNjb3JlICh1c2VkIGZvciBuZXR3b3JrIHNjb3JlIG9wdGltaXphdGlvbikuCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTR9CnBsb3QobWNtYy5vdXQsbWF4LnNjb3JlPVRSVUUpCmBgYAoKQnV0IHRoZSBtYWpvciBhZHZhbnRhZ2Ugb2YgdGhpcyBtZXRob2QgaXMgdGhlIHBvc3NpYmlsaXR5IG9mIHF1ZXJ5aW5nIHRoZSBNQ01DIHNhbXBsZSB1c2luZyBhIGZvcm11bGEgc3RhdGVtZW50LgoKYGBge3IsIGVjaG89VFJVRSwgZXZhbD1UUlVFfQojIGF2ZXJhZ2UgaW5kaXZpZHVhbCBhcmMgc3VwcG9ydApxdWVyeShtY21jYWJuID0gbWNtYy5vdXQpCgojIHByb2JhYmlsaXR5IHRoYXQgd29ybSBjb3VudCBiZWluZyBsaW5rZWQgdG8gYWdlIGJ1dCBub3QgdG8gZmVtYWxlIGRpcmVjdGx5CnF1ZXJ5KG1jbWNhYm4gPSBtY21jLm91dCxmb3JtdWxhID0gfndvcm1Db3VudHxhZ2Utd29ybUNvdW50fGZlbWFsZSkKCiMgcHJvYmFiaWxpdHkgdGhhdCB3b3JtIGNvdW50IGJlaW5nIGRpcmVjdGx5IGxpbmtlZCB0byBhZ2UgYW5kIGFkZyBhbmQgdGhhdCBhZGcgaXMgbGluayB0byBhZ2UgKHVuZGlyZWN0ZWQpCnF1ZXJ5KG1jbWNhYm4gPSBtY21jLm91dCxmb3JtdWxhID0gfndvcm1Db3VudHxhZ2UgKyB3b3JtQ291bnR8YWRnICsgYWdlfGFkZykrCiAgcXVlcnkobWNtY2FibiA9IG1jbWMub3V0LGZvcm11bGEgPSB+d29ybUNvdW50fGFnZSArIHdvcm1Db3VudHxhZGcgKyBhZGd8YWdlKQpgYGAKCgojIFJlZmVyZW5jZXMKCg==