// t-SNE Algorithm var tsnejs = tsnejs || { REVISION: 'ALPHA' }; (function(global) { "use strict"; // utility function var assert = function(condition, message) { if (!condition) { throw message || "Assertion failed"; } } // syntax sugar var getopt = function(opt, field, defaultval) { if(opt.hasOwnProperty(field)) { return opt[field]; } else { return defaultval; } } // return 0 mean unit standard deviation random number var return_v = false; var v_val = 0.0; var gaussRandom = function() { if(return_v) { return_v = false; return v_val; } var u = 2*Math.random()-1; var v = 2*Math.random()-1; var r = u*u + v*v; if(r == 0 || r > 1) return gaussRandom(); var c = Math.sqrt(-2*Math.log(r)/r); v_val = v*c; // cache this for next function call for efficiency return_v = true; return u*c; } // return random normal number var randn = function(mu, std){ return mu+gaussRandom()*std; } // utilitity that creates contiguous vector of zeros of size n var zeros = function(n) { if(typeof(n)==='undefined' || isNaN(n)) { return []; } if(typeof ArrayBuffer === 'undefined') { // lacking browser support var arr = new Array(n); for(var i=0;i 1e-7) Hhere -= pj * Math.log(pj); } // adjust beta based on result if(Hhere > Htarget) { // entropy was too high (distribution too diffuse) // so we need to increase the precision for more peaky distribution betamin = beta[i]; // move up the bounds if(betamax === Infinity) { beta[i] = beta[i] * 2; } else { beta[i] = (beta[i] + betamax) / 2; } } else { // converse case. make distrubtion less peaky betamax = beta[i]; if(betamin === -Infinity) { beta = beta / 2; } else { beta[i] = (beta[i] + betamin) / 2; } } // stopping conditions: too many tries or got a good precision num++; if(Math.abs(Hhere - Htarget) < tol) { done = true; } if(num >= maxtries) { done = true; } } //console.log('data point ' + i + ' gets precision ' + beta[i] + ' after ' + num + ' binary search steps.'); // copy over the final prow to P at row i for(var j=0;j 0 ? 1 : x < 0 ? -1 : 0; } var tSNE = function(opt) { var opt = opt || {}; this.perplexity = getopt(opt, "perplexity", 30); // effective number of nearest neighbors this.dim = getopt(opt, "dim", 2); // by default 2-D tSNE this.epsilon = getopt(opt, "epsilon", 10); // learning rate this.iter = 0; } tSNE.prototype = { // this function takes a given distance matrix and creates // matrix P from them. // D is assumed to be provided as a list of lists, and should be symmetric initDataDist: function(D) { var N = D.length; assert(N > 0, " X is empty? You must have some data!"); // convert D to a (fast) typed array version var dists = zeros(N * N); // allocate contiguous array for(var i=0;i