Revision 4da45df4dc3ac25061731e83afc76c9970029794 authored by Andreas Stuhlmüller on 02 July 2017, 20:13:26 UTC, committed by GitHub on 02 July 2017, 20:13:26 UTC
More sample/dist arg checks
util.js
'use strict';
var _ = require('lodash');
var assert = require('assert');
var seedrandom = require('seedrandom');
var ad = require('./ad');
var Tensor = require('./tensor');
var rng = Math.random;
var trampolineRunners = {
web: function(yieldEvery) {
yieldEvery = yieldEvery || 100;
var f = function(t, wrappedF) {
var lastPauseTime = Date.now();
if (f.__cancel__) {
f.__cancel__ = false;
} else {
while (t) {
var currTime = Date.now();
if (currTime - lastPauseTime > yieldEvery) {
// NB: return is crucial here as it exits the while loop
// and i'm using return rather than break because we might
// one day want to cancel the timer
return setTimeout(function() { wrappedF(t, wrappedF); }, 0);
} else {
t = t();
}
}
}
};
return f;
},
cli: function() {
return function(t) {
while (t) {
t = t()
}
};
}
}
function random() {
return rng();
}
function seedRNG(seed) {
rng = seedrandom(seed);
}
function resetRNG() {
rng = Math.random;
}
function assertValidRandomSeed(seed) {
var msg = 'Random seed should be a positive integer.';
assert(_.isFinite(seed) && seed >= 0, msg);
}
function runningInBrowser() {
return (typeof window !== 'undefined');
}
function makeGensym() {
var seq = 0;
return function(prefix) {
var result = prefix + seq;
seq += 1;
return result;
};
}
var gensym = makeGensym();
function prettyJSON(obj) {
console.log(JSON.stringify(obj, null, 2));
}
function asArray(arg) {
return arg ? [].concat(arg) : [];
}
function sum(xs) {
if (xs.length === 0) {
return 0.0;
} else {
var total = _.reduce(xs,
function(a, b) {
return a + b;
});
return total;
}
}
function product(xs) {
var result = 1;
for (var i = 0, n = xs.length; i < n; i++) {
result *= xs[i];
}
return result;
}
function logsumexp(a) {
var m = Math.max.apply(null, a);
var sum = 0;
for (var i = 0; i < a.length; ++i) {
sum += (a[i] === -Infinity ? 0 : Math.exp(a[i] - m));
}
return m + Math.log(sum);
}
var deleteIndex = function(arr, i) {
return arr.slice(0, i).concat(arr.slice(i + 1))
}
// func(x, i, xs, cont)
// cont()
function cpsForEach(func, cont, xs, i) {
i = (i === undefined) ? 0 : i;
if (i === xs.length) {
return cont();
} else {
return func(xs[i], i, xs, function() {
return function() { // insert trampoline step
return cpsForEach(func, cont, xs, i + 1);
};
});
}
}
function cpsLoop(n, func, cont) {
function loop(i) {
if (i === n) {
return cont();
} else {
return func(i, function() {
return function() { // insert trampoline step
return loop(i + 1);
};
});
}
}
assert(_.isNumber(n), 'Number expected.');
return loop(0);
}
function cpsIterate(n, initial, func, cont) {
var val = initial;
return cpsLoop(n,
function(i, next) {
return func(function(nextVal) {
val = nextVal;
return next();
}, val);
},
function() { return cont(val); });
}
function histExpectation(hist, func) {
var f = func || _.identity;
return _.reduce(hist, function(acc, obj) {
return acc + obj.prob * f(obj.val);
}, 0);
}
function histStd(hist) {
var m = histExpectation(hist);
return Math.sqrt(histExpectation(hist, function(x) {
return Math.pow(x - m, 2);
}));
}
function sameKeys(obj1, obj2) {
return _.size(obj1) === _.size(obj2) &&
_.every(_.keys(obj1), function(key) { return _.has(obj2, key); });
}
function histsApproximatelyEqual(actualHist, expectedHist, tolerance, exactSupport) {
if (expectedHist === undefined || actualHist === undefined) {
return false;
}
if (exactSupport && !sameKeys(actualHist, expectedHist)) {
return false;
}
return _.every(expectedHist, function(expectedValue, key) {
var value = actualHist[key] || 0;
return Math.abs(value - expectedValue) <= tolerance;
});
}
function mergeDefaults(options, defaults, callerName) {
if (callerName) {
if (options !== undefined && !_.isObject(options)) {
var msg = callerName + ' expected an options object but received: ' + JSON.stringify(options);
throw new Error(msg);
}
var extra = _.difference(_.keys(options), _.keys(defaults));
extra.forEach(function(name) {
warn('Warning: Unused option \"' + name + '\" given to ' + callerName + '.');
});
}
return _.defaults(options ? _.clone(options) : {}, defaults);
}
// When using an object to fake named function parameters we sometimes
// accept a string *or* and object as a way of passing both a string
// *and* a related set of sub options. This helper takes such a value,
// extracts the string and object, passes them through a continuation
// and returns the result.
// getValAndOpts('foo', (name, opts) => [name, opts])
// => ['foo', {}]
// getValAndOpts({foo: {bar: 0}}, (name, opts) => [name, opts])
// => ['foo', {bar: 0}]
function getValAndOpts(obj, cont) {
var args;
if (_.isString(obj)) {
args = [obj, {}];
} else {
if (_.size(obj) !== 1) {
throw 'Expected an object with a single key but received: ' + JSON.stringify(obj);
}
var key = _.keys(obj)[0];
args = [key, obj[key]];
}
return cont.apply(null, args);
}
function InfToJSON(k, v) {
if (v === Infinity) {
return 'Infinity';
} else if (v === -Infinity) {
return '-Infinity';
} else {
return v;
}
}
function InfFromJSON(k, v) {
if (v === 'Infinity') {
return Infinity;
} else if (v === '-Infinity') {
return -Infinity;
} else {
return v;
}
}
function serialize(o) {
return JSON.stringify(o, InfToJSON);
}
function deserialize(o) {
return JSON.parse(o, InfFromJSON);
}
function time(name, thunk) {
if (console.time) {
console.time(name);
var ret = thunk();
console.timeEnd(name);
return ret;
} else {
return thunk();
}
}
function timeif(bool, name, thunk) {
return bool ? time(name, thunk) : thunk();
}
var warningsIssued = {};
function resetWarnings() {
warningsIssued = {};
}
function warn(msg, onceOnly) {
if (!global.suppressWarnings &&
(!onceOnly || !_.has(warningsIssued, msg))) {
console.warn(msg);
if (onceOnly) {
warningsIssued[msg] = true;
}
}
}
function error(msg) {
throw new Error(msg);
}
function jsthrow(obj) {
throw obj;
}
// Equivalent to Number.isInteger(), which isn't available in the
// version of phantom.js used on Travis at the time of writing.
function isInteger(x) {
return typeof x === 'number' &&
isFinite(x) &&
Math.floor(x) === x;
}
// Unlike _.isObject this returns false for arrays and functions.
function isObject(x) {
return x !== undefined &&
x !== null &&
typeof x === 'object' && // required for Node <= 0.12
Object.getPrototypeOf(x) === Object.prototype;
}
function isTensor(t) {
return t instanceof Tensor;
}
function isMatrix(t) {
return t instanceof Tensor && t.rank === 2;
}
function isVector(t) {
return t instanceof Tensor && t.rank === 2 && t.dims[1] === 1;
}
function tensorEqDim0(v, w) {
// Useful for checking two vectors have the same length, or that the
// dimension of a vector and matrix match.
return v.dims[0] === w.dims[0];
}
function tensorEqDims(t1, t2) {
if (t1.dims.length !== t2.dims.length) {
return false;
}
for (var i = 0; i < t1.dims.length; i++) {
if (t1.dims[i] !== t2.dims[i]) {
return false;
}
}
return true;
}
function idMatrix(n) {
if (n <= 0) {
throw new Error('n should be > 0.');
}
var out = new Tensor([n, n]);
for (var i = 0; i < n; i++) {
out.data[i * (n + 1)] = 1;
}
return out;
}
function oneHot(index, length) {
if (length <= 0) {
throw new Error('length should be > 0.');
}
if (index < 0 || index >= length) {
throw new Error('index out of bounds');
}
var out = new Tensor([length, 1]);
out.data[index] = 1;
return out;
}
function relativizeAddress(baseAddress, address) {
assert.ok(address.slice(0, baseAddress.length) === baseAddress, 'Address prefix mismatch.');
return address.slice(baseAddress.length);
}
module.exports = {
trampolineRunners: trampolineRunners,
random: random,
seedRNG: seedRNG,
resetRNG: resetRNG,
assertValidRandomSeed: assertValidRandomSeed,
cpsForEach: cpsForEach,
cpsLoop: cpsLoop,
cpsIterate: cpsIterate,
histExpectation: histExpectation,
histStd: histStd,
histsApproximatelyEqual: histsApproximatelyEqual,
gensym: gensym,
logsumexp: logsumexp,
deleteIndex: deleteIndex,
makeGensym: makeGensym,
prettyJSON: prettyJSON,
runningInBrowser: runningInBrowser,
mergeDefaults: mergeDefaults,
getValAndOpts: getValAndOpts,
sum: sum,
product: product,
asArray: asArray,
serialize: serialize,
deserialize: deserialize,
timeif: timeif,
warn: warn,
resetWarnings: resetWarnings,
error: error,
jsthrow: jsthrow,
isInteger: isInteger,
isObject: isObject,
isTensor: isTensor,
isVector: isVector,
isMatrix: isMatrix,
tensorEqDim0: tensorEqDim0,
tensorEqDims: tensorEqDims,
idMatrix: idMatrix,
oneHot: oneHot,
relativizeAddress: relativizeAddress
};
Computing file changes ...