bayesian-bandit
Advanced tools
Comparing version 0.9.1 to 0.10.0
@@ -9,8 +9,19 @@ (function(exports) { | ||
function Arm() { } | ||
/** | ||
* @param {object} [args] | ||
* @param {int} [args.count] - number of attempts on this arm | ||
* @param {number} [args.sum] - total value accumulated on this arm | ||
* @constructor | ||
*/ | ||
function Arm(args) { | ||
this.count = args && args.count || 0 | ||
this.sum = args && args.sum || 0 | ||
} | ||
Arm.prototype.count = 0 | ||
Arm.prototype.sum = 0 | ||
/** | ||
* @param {number} value | ||
* | ||
* Increments number of attempts on the arm by one and the total accumulated | ||
* value by value | ||
*/ | ||
Arm.prototype.reward = function(value) { | ||
@@ -22,2 +33,17 @@ | ||
/** | ||
* @param {int} numTries | ||
* @param {number} totalValue | ||
* | ||
* Increments the number of attempts on the arm and the total value | ||
* accumulated during those attempts. | ||
*/ | ||
Arm.prototype.rewardMultiple = function(numTries, totalValue) { | ||
this.count += numTries | ||
this.sum += totalValue | ||
} | ||
/** | ||
* @returns {number} between 0 and 1 | ||
*/ | ||
Arm.prototype.sample = function() { | ||
@@ -27,15 +53,20 @@ return this.rbeta(1 + this.sum, 1 + this.count - this.sum) | ||
/** | ||
* @param {int} a | ||
* @param {int} b | ||
* @returns {number} - number between 0 and 1 | ||
* | ||
* Adapted from https://github.com/CamDavidsonPilon/Probabilistic-Programming-and-Bayesian-Methods-for-Hackers/master/Chapter6_Priorities/d3bandits.js, | ||
* which references Simulation and MC, Wiley. | ||
* | ||
* This apparently generates random numbers from a beta distribution: | ||
* http://en.wikipedia.org/wiki/Beta_distribution | ||
* I don't really know why this works -- it's just adapted from d3bandits.js | ||
* | ||
* For comparison, you might want to review the R project's rbeta code: | ||
* https://svn.r-project.org/R/trunk/src/nmath/rbeta.c | ||
* | ||
*/ | ||
Arm.prototype.rbeta = function(a, b) { | ||
// Adapted from https://github.com/CamDavidsonPilon/Probabilistic-Programming-and-Bayesian-Methods-for-Hackers/master/Chapter6_Priorities/d3bandits.js, | ||
// which references Simulation and MC, Wiley. | ||
// | ||
// This apparently generates random numbers from a beta distribution: | ||
// http://en.wikipedia.org/wiki/Beta_distribution | ||
// I don't really know why this works | ||
// -- it's just adapted from d3bandits.js. | ||
// | ||
// For comparison, you might want to review the R project's rbeta code: | ||
// https://svn.r-project.org/R/trunk/src/nmath/rbeta.c | ||
var sum = a + b | ||
@@ -72,2 +103,16 @@ , ratio = a / b | ||
/** | ||
* @param {object} [options] | ||
* @param {Array.<{count: int, sum: number}>} [options.arms] | ||
* - Initialize the bandit with arms specified by the data provided | ||
* | ||
* @param {int} [options.numberOfArms] | ||
* - Initialize the bandit with a number of empty arms | ||
* | ||
* Note, only one of options.arms and options.numberOfArms should | ||
* be provided. If both are provided, options.arms takes precedence and | ||
* numberOfArms will be ignored. | ||
* | ||
* @constructor | ||
*/ | ||
function Bandit(options) { | ||
@@ -77,7 +122,17 @@ | ||
for(var a = 0; a < (options || {}).numberOfArms; a++) { | ||
this.arms.push(new Arm()) | ||
// If options.arms is explicitly passed, initialize the arms array with it | ||
if (options && options.arms) { | ||
for (var i = 0; i < options.arms.length; i++) { | ||
this.arms.push(new Arm(options.arms[i])) | ||
} | ||
} else { // Otherwise initialize empty arms based on options.numberOfArms | ||
for (var a = 0; a < (options || {}).numberOfArms; a++) { | ||
this.arms.push(new Arm()) | ||
} | ||
} | ||
} | ||
/** | ||
* @returns {int} - index of the arm chosen by the bandit algorithm | ||
*/ | ||
Bandit.prototype.selectArm = function() { | ||
@@ -84,0 +139,0 @@ |
{ | ||
"name": "bayesian-bandit", | ||
"version": "0.9.1", | ||
"version": "0.10.0", | ||
"description": "Bayesian bandit implementation for Node and the browser.", | ||
@@ -10,2 +10,5 @@ "main": "./bayesian-bandit.js", | ||
}, | ||
"scripts": { | ||
"test": "nodeunit tests.js" | ||
}, | ||
"keywords": [ | ||
@@ -29,3 +32,6 @@ "machine", | ||
}, | ||
"author": "omphalos" | ||
"author": "omphalos", | ||
"devDependencies": { | ||
"nodeunit": "^0.9.1" | ||
} | ||
} |
34
tests.js
@@ -31,2 +31,14 @@ var testCase = require('nodeunit').testCase | ||
'rewardMultiple should increase the sum and count': function(test) { | ||
var arm = new Bandit.Arm() | ||
arm.rewardMultiple(10, 2) | ||
arm.rewardMultiple(15, 5) | ||
test.equal(arm.count, 25) | ||
test.equal(arm.sum, 7) | ||
test.done() | ||
}, | ||
'sample should sample from the rbeta distribution': function(test) { | ||
@@ -142,2 +154,24 @@ | ||
'new Bandit with options.arms should initialized arms properly': | ||
function(test) { | ||
var bandit = new Bandit({ | ||
arms:[ | ||
{count: 100, sum: 20}, | ||
{count: 50, sum: 30}, | ||
{count: 70, sum: 10} | ||
] | ||
}) | ||
test.equal(bandit.arms.length, 3) | ||
test.equal(bandit.arms[0].count, 100) | ||
test.equal(bandit.arms[0].sum, 20) | ||
test.equal(bandit.arms[1].count, 50) | ||
test.equal(bandit.arms[1].sum, 30) | ||
test.equal(bandit.arms[2].count, 70) | ||
test.equal(bandit.arms[2].sum, 10) | ||
test.done() | ||
}, | ||
'selectArm should return the arm with the largest sample': function(test) { | ||
@@ -144,0 +178,0 @@ |
Sorry, the diff of this file is not supported yet
13439
6
302
61
1