// We take back our previous example, with unfair coin var baserate = 0.1 // We try the rejection method. If the baserate is too low, most samples will be rejected. var infModel = function(){ Infer({method: 'rejection', samples: 100}, function(){ var A = flip(baserate) var B = flip(baserate) var C = flip(baserate) condition(A+B+C >= 2) return A}) } //With the lodash library, we can compute the average time of an inference algorithm var time = function(foo, trials) { var start = _.now() var ret = repeat(trials, foo) var end = _.now() return (end-start)/trials } time(infModel, 10) // Try to lower the baserate to 0.01, it should already be slow.On this example, we could also use "enumerate"
///fold: var time = function(foo, trials) { var start = _.now() var ret = repeat(trials, foo) var end = _.now() return (end-start)/trials } /// var baserate = 0.1 var infModel = function(){ Infer({method: 'enumerate'}, function(){ var A = flip(baserate) var B = flip(baserate) var C = flip(baserate) condition(A+B+C >= 2) return A}) } time(infModel, 10) // Try to lower the baserate. What is the impact on time ? Why ?
///fold: //a timing utility: run 'foo' 'trials' times, report average time. var time = function(foo, trials) { var start = _.now() var ret = repeat(trials, foo) var end = _.now() return (end-start)/trials } /// // However, enumerate does not scale well with the number of samples. // Let us modify the program to test this var baserate = 0.1 var numFlips = 3 var infModel = function(){ Infer({method: 'enumerate'}, function(){ var choices = repeat(numFlips, function(){flip(baserate)}) // Sum computes the sum of all elements in an array condition(sum(choices) >= 2) return choices[0]}) } time(infModel, 10) // Try this program with 15 flips. And for rejection, what is the impact of increasing the number of flips ? Why ?
///fold: //a timing utility: run 'foo' 'trials' times, report average time. var time = function(foo, trials) { var start = _.now() var ret = repeat(trials, foo) var end = _.now() return (end-start)/trials } /// // Finally, let's try the MCMC algorithm this time. var baserate = 0.1 var numFlips = 3 var infModel = function(){ Infer({method: 'MCMC', lag: 100}, function(){ var choices = repeat(numFlips, function(){flip(baserate)}) condition(sum(choices) >= 2) return choices[0]}) } time(infModel, 10) // You can even try baserate = 0.001 and numFlips = 100 and it should still be rather efficient. // However, what we cannot see here is that what we gain in efficiency, we lose in precision. // But this is a standard tradeoff.
//Try to run this model. var model = function(){ var trueX = sample(Gaussian({mu: 0, sigma: 1})) var obsX = sample(Gaussian({mu: trueX, sigma: 0.1})) condition(obsX == 0.2) return trueX } viz(Infer({method: 'rejection', samples:1}, model)) // This should fail. Indeed, the condition is almost impossible to satisfy, a random sample will not satisfy the condition.
// With oberve, this will work. var model = function(){ var trueX = sample(Gaussian({mu: 0, sigma: 1})) observe(Gaussian({mu: trueX, sigma: 0.1}), 0.2) return trueX } viz(Infer({method: 'rejection', samples:1000, maxScore: 2}, model)) //However, you need to precise that the "maxScore" should be different from the default value ("0"). //Try to remove this option, why does rejection sampling "naively" should not work ?
// Here is a simple Markov Chain var states = ['S', 'C']; var transitionProbs = {S: [.3, .7], C: [.5, .5]} // The categorical distribution is exactly what we need here var transition = function(state){ return categorical({vs: states, ps: transitionProbs[state]}) } // We repeat n times the transition function var chain = function(state, n){ return (n == 0 ? state : chain(transition(state), n-1)) } print("State after 10 steps:") viz.hist(repeat(1000,function() {chain('S',10)})) viz.hist(repeat(1000,function() {chain('C',10)})) print("State after 25 steps:") viz.hist(repeat(1000,function() {chain('S',25)})) viz.hist(repeat(1000,function() {chain('C',25)})) print("State after 50 steps:") viz.hist(repeat(1000,function() {chain('S',50)})) viz.hist(repeat(1000,function() {chain('C',50)}))
Metropolis Hasting on the Example of Trick Dice.
// Dice var dice = function() { return randomInteger(6) + 1 } // Computing the Acceptance Rate for our example var alpha = function(n,m) { return Math.min(1,((m == 6) ? 5 : 1) / ((n == 6) ? 5 : 1)) } // The MH algorithm var myMH = function(Nsamples) { var myMHloop = function(n,l) { if (n == 0) {return l} else { var x = l[l.length - 1]; var y = dice(); (flip(alpha(x,y))) ? l.push(y) : l.push(x) ; myMHloop(n-1,l) } } return myMHloop(Nsamples,[1]) } print(myMH(1000)) viz(myMH(1000)) // Remark that the frequencies are correct, but the samples are not independent (big blocks of 6)!
// Geometric Distribution var geometric = function() { (flip()) ? 0 : (1 + geometric()) } viz(repeat(10000,geometric))
// Computing the acceptance rate for the Geometric Distribution var alpha = function(n,i) { return (n == 0) ? 1/4 :((i == 1) ? 1/2 : 1) } // The MH algorithm var myMH = function(Nsamples) { var myMHloop = function(n,l) { if (n == 0) {return l} else { var x = l[l.length - 1]; var y = (x == 0) ? 1 : ((flip()) ? x+1 : x-1); (flip(alpha(x,y-x))) ? l.push(y) : l.push(x) ; myMHloop(n-1,l) } } return myMHloop(Nsamples,[0]) } print(myMH(10000)) viz(myMH(10000)) // You can try to change the initial state and look at the first samples