diff --git a/bower.json b/bower.json index 07f57f5..b2c86a8 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "ml", - "version": "0.9.1", + "version": "0.10.0", "main": [ "dist/ml.js", "dist/ml.min.js" diff --git a/dist/ml.js b/dist/ml.js index e8a95d4..517f478 100644 --- a/dist/ml.js +++ b/dist/ml.js @@ -74,6 +74,7 @@ return /******/ (function(modules) { // webpackBootstrap Math.SG = __webpack_require__(97); Math.Matrix = exports.Matrix; Math.SparseMatrix = __webpack_require__(99); + Math.CurveFitting = __webpack_require__(100); // Statistics packages @@ -81,36 +82,37 @@ return /******/ (function(modules) { // webpackBootstrap Stat.array = __webpack_require__(6); Stat.matrix = __webpack_require__(7); - Stat.PCA = __webpack_require__(100); - Stat.Performance = __webpack_require__(105); + Stat.PCA = __webpack_require__(122); + Stat.Performance = __webpack_require__(127); // Random number generation var RNG = exports.RNG = {}; - RNG.XSadd = __webpack_require__(107); + RNG.XSadd = __webpack_require__(129); // Supervised learning var SL = exports.SL = {}; - SL.SVM = __webpack_require__(108); - SL.KNN = __webpack_require__(111); - SL.NaiveBayes = __webpack_require__(114); - SL.PLS = __webpack_require__(119); + SL.SVM = __webpack_require__(130); + SL.KNN = __webpack_require__(133); + SL.NaiveBayes = __webpack_require__(136); + SL.PLS = __webpack_require__(141); // Clustering var Clust = exports.Clust = {}; - Clust.kmeans = __webpack_require__(126); - Clust.hclust = __webpack_require__(128); + Clust.kmeans = __webpack_require__(145); + Clust.hclust = __webpack_require__(147); // Neural networks var NN = exports.NN = exports.nn = {}; - NN.SOM = __webpack_require__(138); - NN.FNN = __webpack_require__(141); + NN.SOM = __webpack_require__(157); + NN.FNN = __webpack_require__(160); + /***/ }, @@ -8966,4104 +8968,10548 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, /* 100 */ -/***/ function(module, exports, __webpack_require__) { - - module.exports = __webpack_require__(101); - - -/***/ }, -/* 101 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; - var Matrix = __webpack_require__(14); - var Stat = __webpack_require__(102); - var SVD = Matrix.DC.SVD; - module.exports = PCA; + var LM = __webpack_require__(101); + var math = LM.Matrix.algebra; + var Matrix = __webpack_require__(113); /** - * Creates new PCA (Principal Component Analysis) from the dataset - * @param {Matrix} dataset - * @param {Object} options - options for the PCA algorithm - * @param {boolean} reload - for load purposes - * @param {Object} model - for load purposes - * @constructor - * */ - function PCA(dataset, options, reload, model) { + * This function calculates the spectrum as a sum of lorentzian functions. The Lorentzian + * parameters are divided in 3 batches. 1st: centers; 2nd: heights; 3th: widths; + * @param t Ordinate values + * @param p Lorentzian parameters + * @param c Constant parameters(Not used) + * @returns {*} + */ + function sumOfLorentzians(t,p,c){ + var nL = p.length/3,factor,i, j,p2, cols = t.rows; + var result = Matrix.zeros(t.length,1); - if (reload) { - this.U = model.U; - this.S = model.S; - this.means = model.means; - this.std = model.std; - this.standardize = model.standardize - } else { - if(options === undefined) { - options = { - standardize: false - }; + for(i=0;i this.U.columns) - throw new RangeError("the number of dimensions must not be larger than " + this.U.columns); + * * Fits a set of points to a Lorentzian function. Returns the center of the peak, the width at half height, and the height of the signal. + * @param data,[y] + * @returns {*[]} + */ + function optimizeSingleLorentzian(xy, peak, opts) { + opts = opts || {}; + var xy2 = parseData(xy, opts.percentage||0); - if (!Matrix.isMatrix(dataset)) { - dataset = new Matrix(dataset); - } else { - dataset = dataset.clone(); + if(xy2===null||xy2[0].rows<3){ + return null; //Cannot run an optimization with less than 3 points } - var X = adjust(dataset, this.standardize).result; - return X.mmul(this.U.subMatrix(0, this.U.rows - 1, 0, dimensions)); - }; + var t = xy2[0]; + var y_data = xy2[1]; + var maxY = xy2[2]; + var nbPoints = t.rows, i; - /** - * This method returns the percentage variance of each eigenvector. - * @return {Number} percentage variance of each eigenvector. - * */ - PCA.prototype.getExplainedVariance = function () { - var sum = this.S.reduce(function (previous, value) { - return previous + value; - }); - return this.S.map(function (value) { - return value / sum; - }); - }; + var weight = [nbPoints / Math.sqrt(y_data.dot(y_data))]; - /** - * Function that returns the Eigenvectors of the covariance matrix. - * @returns {Matrix} - */ - PCA.prototype.getEigenvectors = function () { - return this.U; - }; + var opts=Object.create(opts.LMOptions || [ 3, 100, 1e-3, 1e-3, 1e-3, 1e-2, 1e-2, 11, 9, 1 ]); + //var opts = [ 3, 100, 1e-3, 1e-3, 1e-3, 1e-2, 1e-2, 11, 9, 1 ]; + var consts = [ ]; + var dt = Math.abs(t[0][0]-t[1][0]);// optional vector of constants + var dx = new Matrix([[-dt/10000],[-1e-3],[-dt/10000]]);//-Math.abs(t[0][0]-t[1][0])/100; + var p_init = new Matrix([[peak.x],[1],[peak.width]]); + var p_min = new Matrix([[peak.x-dt],[0.75],[peak.width/4]]); + var p_max = new Matrix([[peak.x+dt],[1.25],[peak.width*4]]); + + var p_fit = LM.optimize(singleLorentzian,p_init,t,y_data,weight,dx,p_min,p_max,consts,opts); - /** - * Function that returns the Eigenvalues (on the diagonal). - * @returns {*} - */ - PCA.prototype.getEigenvalues = function () { - return this.S; - }; + + p_fit = p_fit.p; + return [p_fit[0],[p_fit[1][0]*maxY],p_fit[2]]; + + } /** - * This method returns a dataset normalized in the following form: - * X = (X - mean) / std - * @param dataset. - * @param {Boolean} standarize - do standardization - * @return A dataset normalized. - * */ - function adjust(dataset, standarize) { - var means = Stat.matrix.mean(dataset); - var std = standarize ? Stat.matrix.standardDeviation(dataset, means, true) : undefined; + * Fits a set of points to a gaussian bell. Returns the mean of the peak, the std and the height of the signal. + * @param data,[y] + * @returns {*[]} + */ + function optimizeSingleGaussian(xy, peak, opts) { + opts = opts || {}; + var xy2 = parseData(xy, opts.percentage||0); - var result = dataset.subRowVector(means); - return { - result: standarize ? result.divRowVector(std) : result, - means: means, - std: std + if(xy2===null||xy2[0].rows<3){ + return null; //Cannot run an optimization with less than 3 points } - } + var t = xy2[0]; + var y_data = xy2[1]; + var maxY = xy2[2]; -/***/ }, -/* 102 */ -/***/ function(module, exports, __webpack_require__) { + var nbPoints = t.rows, i; - 'use strict'; - exports.array = __webpack_require__(103); - exports.matrix = __webpack_require__(104); + var weight = [nbPoints / Math.sqrt(y_data.dot(y_data))]; -/***/ }, -/* 103 */ -/***/ function(module, exports) { + var opts=Object.create(opts.LMOptions || [ 3, 100, 1e-3, 1e-3, 1e-3, 1e-2, 1e-2, 11, 9, 1 ]); + //var opts = [ 3, 100, 1e-3, 1e-3, 1e-3, 1e-2, 1e-2, 11, 9, 1 ]; + var consts = [ ]; // optional vector of constants + var dt = Math.abs(t[0][0]-t[1][0]); + var dx = new Matrix([[-dt/10000],[-1e-3],[-dt/10000]]);//-Math.abs(t[0][0]-t[1][0])/100; - 'use strict'; + var dx = new Matrix([[-Math.abs(t[0][0]-t[1][0])/1000],[-1e-3],[-peak.width/1000]]); + var p_init = new Matrix([[peak.x],[1],[peak.width]]); + var p_min = new Matrix([[peak.x-dt],[0.75],[peak.width/4]]); + var p_max = new Matrix([[peak.x+dt],[1.25],[peak.width*4]]); + //var p_min = new Matrix([[peak.x-peak.width/4],[0.75],[peak.width/3]]); + //var p_max = new Matrix([[peak.x+peak.width/4],[1.25],[peak.width*3]]); - function compareNumbers(a, b) { - return a - b; + var p_fit = LM.optimize(singleGaussian,p_init,t,y_data,weight,dx,p_min,p_max,consts,opts); + p_fit = p_fit.p; + return [p_fit[0],[p_fit[1][0]*maxY],p_fit[2]]; } - /** - * Computes the sum of the given values - * @param {Array} values - * @returns {number} + /* + peaks on group should sorted */ - exports.sum = function sum(values) { - var sum = 0; - for (var i = 0; i < values.length; i++) { - sum += values[i]; + function optimizeLorentzianTrain(xy, group, opts){ + var xy2 = parseData(xy); + //console.log(xy2[0].rows); + if(xy2===null||xy2[0].rows<3){ + return null; //Cannot run an optimization with less than 3 points + } + + var t = xy2[0]; + var y_data = xy2[1]; + var maxY = xy2[2]; + var currentIndex = 0; + var nbPoints = t.length; + var nextX; + var tI, yI, maxY; + var result=[], current; + for(var i=0; i max) max = values[i]; - } - return max; - }; + return result; - /** - * Computes the minimum of the given values - * @param {Array} values - * @returns {number} - */ - exports.min = function min(values) { - var min = Infinity; - var l = values.length; - for (var i = 0; i < l; i++) { - if (values[i] < min) min = values[i]; + } + + function optimizeGaussianTrain(xy, group, opts){ + var xy2 = parseData(xy); + //console.log(xy2[0].rows); + if(xy2===null||xy2[0].rows<3){ + return null; //Cannot run an optimization with less than 3 points } - return min; - }; - /** - * Computes the min and max of the given values - * @param {Array} values - * @returns {{min: number, max: number}} - */ - exports.minMax = function minMax(values) { - var min = Infinity; - var max = -Infinity; - var l = values.length; - for (var i = 0; i < l; i++) { - if (values[i] < min) min = values[i]; - if (values[i] > max) max = values[i]; + var t = xy2[0]; + var y_data = xy2[1]; + var maxY = xy2[2]; + var currentIndex = 0; + var nbPoints = t.length; + var nextX; + var tI, yI, maxY; + var result=[], current; + for(var i=0; imaxY) + maxY = y[i]; + } } - sum += 1 / values[i]; - } - return l / sum; - }; + else{ + //It is a colum matrix + if(typeof x[0] === "object"){ + for(i=0;imaxY) + maxY = y[i][0]; + } + } - /** - * Computes the contraharmonic mean of the given values - * @param {Array} values - * @returns {number} - */ - exports.contraHarmonicMean = function contraHarmonicMean(values) { - var r1 = 0; - var r2 = 0; - var l = values.length; - for (var i = 0; i < l; i++) { - r1 += values[i] * values[i]; - r2 += values[i]; + } + + //} } - if (r2 < 0) { - throw new RangeError('sum of values is negative'); + else{ + //Looks like a column wise matrix [[x],[y]] + var nbPoints = nbSeries; + //if(nbPoints<3) + // throw new SizeException(nbPoints); + //else { + t = new Array(nbPoints);//new Matrix(nbPoints, 1); + y_data = new Array(nbPoints);//new Matrix(nbPoints, 1); + for (i = 0; i < nbPoints; i++) { + t[i] = xy[i][0]; + y_data[i] = xy[i][1]; + if(y_data[i]>maxY) + maxY = y_data[i]; + } + //} } - return r1 / r2; - }; - - /** - * Computes the median of the given values - * @param {Array} values - * @param {boolean} [alreadySorted=false] - * @returns {number} - */ - exports.median = function median(values, alreadySorted) { - if (alreadySorted === undefined) alreadySorted = false; - if (!alreadySorted) { - values = values.slice().sort(compareNumbers); + for (i = 0; i < nbPoints; i++) { + y_data[i]/=maxY; } - var l = values.length; - var half = Math.floor(l / 2); - if (l % 2 === 0) { - return (values[half - 1] + values[half]) * 0.5; - } else { - return values[half]; + if(threshold){ + for (i = nbPoints-1; i >=0; i--) { + if(y_data[i]0) + return [(new Matrix([t])).transpose(),(new Matrix([y_data])).transpose(),maxY]; + return null; + } - /** - * Computes the variance of the given values - * @param {Array} values - * @param {boolean} [unbiased=true] - if true, divide by (n-1); if false, divide by n. - * @returns {number} - */ - exports.variance = function variance(values, unbiased) { - if (unbiased === undefined) unbiased = true; - var theMean = exports.mean(values); - var theVariance = 0; - var l = values.length; + function sizeException(nbPoints) { + return new RangeError("Not enough points to perform the optimization: "+nbPoints +"< 3"); + } - for (var i = 0; i < l; i++) { - var x = values[i] - theMean; - theVariance += x * x; - } + module.exports.optimizeSingleLorentzian = optimizeSingleLorentzian; + module.exports.optimizeLorentzianSum = optimizeLorentzianSum; + module.exports.optimizeSingleGaussian = optimizeSingleGaussian; + module.exports.optimizeGaussianSum = optimizeGaussianSum; + module.exports.singleGaussian = singleGaussian; + module.exports.singleLorentzian = singleLorentzian; + module.exports.optimizeGaussianTrain = optimizeGaussianTrain; + module.exports.optimizeLorentzianTrain = optimizeLorentzianTrain; - if (unbiased) { - return theVariance / (l - 1); - } else { - return theVariance / l; - } - }; +/***/ }, +/* 101 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + module.exports = __webpack_require__(102); + module.exports.Matrix = __webpack_require__(103); + module.exports.Matrix.algebra = __webpack_require__(112); + + +/***/ }, +/* 102 */ +/***/ function(module, exports, __webpack_require__) { /** - * Computes the standard deviation of the given values - * @param {Array} values - * @param {boolean} [unbiased=true] - if true, divide by (n-1); if false, divide by n. - * @returns {number} + * Created by acastillo on 8/5/15. */ - exports.standardDeviation = function standardDeviation(values, unbiased) { - return Math.sqrt(exports.variance(values, unbiased)); - }; + var Matrix = __webpack_require__(103); + var math = __webpack_require__(112); + + var DEBUG = false; + /** Levenberg Marquardt curve-fitting: minimize sum of weighted squared residuals + ---------- INPUT VARIABLES ----------- + func = function of n independent variables, 't', and m parameters, 'p', + returning the simulated model: y_hat = func(t,p,c) + p = n-vector of initial guess of parameter values + t = m-vectors or matrix of independent variables (used as arg to func) + y_dat = m-vectors or matrix of data to be fit by func(t,p) + weight = weighting vector for least squares fit ( weight >= 0 ) ... + inverse of the standard measurement errors + Default: sqrt(d.o.f. / ( y_dat' * y_dat )) + dp = fractional increment of 'p' for numerical derivatives + dp(j)>0 central differences calculated + dp(j)<0 one sided 'backwards' differences calculated + dp(j)=0 sets corresponding partials to zero; i.e. holds p(j) fixed + Default: 0.001; + p_min = n-vector of lower bounds for parameter values + p_max = n-vector of upper bounds for parameter values + c = an optional matrix of values passed to func(t,p,c) + opts = vector of algorithmic parameters + parameter defaults meaning + opts(1) = prnt 3 >1 intermediate results; >2 plots + opts(2) = MaxIter 10*Npar maximum number of iterations + opts(3) = epsilon_1 1e-3 convergence tolerance for gradient + opts(4) = epsilon_2 1e-3 convergence tolerance for parameters + opts(5) = epsilon_3 1e-3 convergence tolerance for Chi-square + opts(6) = epsilon_4 1e-2 determines acceptance of a L-M step + opts(7) = lambda_0 1e-2 initial value of L-M paramter + opts(8) = lambda_UP_fac 11 factor for increasing lambda + opts(9) = lambda_DN_fac 9 factor for decreasing lambda + opts(10) = Update_Type 1 1: Levenberg-Marquardt lambda update + 2: Quadratic update + 3: Nielsen's lambda update equations + + ---------- OUTPUT VARIABLES ----------- + p = least-squares optimal estimate of the parameter values + X2 = Chi squared criteria + sigma_p = asymptotic standard error of the parameters + sigma_y = asymptotic standard error of the curve-fit + corr = correlation matrix of the parameters + R_sq = R-squared cofficient of multiple determination + cvg_hst = convergence history + + Henri Gavin, Dept. Civil & Environ. Engineering, Duke Univ. 22 Sep 2013 + modified from: http://octave.sourceforge.net/optim/function/leasqr.html + using references by + Press, et al., Numerical Recipes, Cambridge Univ. Press, 1992, Chapter 15. + Sam Roweis http://www.cs.toronto.edu/~roweis/notes/lm.pdf + Manolis Lourakis http://www.ics.forth.gr/~lourakis/levmar/levmar.pdf + Hans Nielson http://www2.imm.dtu.dk/~hbn/publ/TR9905.ps + Mathworks optimization toolbox reference manual + K. Madsen, H.B., Nielsen, and O. Tingleff + http://www2.imm.dtu.dk/pubdb/views/edoc_download.php/3215/pdf/imm3215.pdf + */ + var LM = { - exports.standardError = function standardError(values) { - return exports.standardDeviation(values) / Math.sqrt(values.length); - }; + optimize: function(func,p,t,y_dat,weight,dp,p_min,p_max,c,opts){ - exports.quartiles = function quartiles(values, alreadySorted) { - if (typeof(alreadySorted) === 'undefined') alreadySorted = false; - if (!alreadySorted) { - values = values.slice(); - values.sort(compareNumbers); - } + var tensor_parameter = 0; // set to 1 of parameter is a tensor - var quart = values.length / 4; - var q1 = values[Math.ceil(quart) - 1]; - var q2 = exports.median(values, true); - var q3 = values[Math.ceil(quart * 3) - 1]; + var iteration = 0; // iteration counter + //func_calls = 0; // running count of function evaluations - return {q1: q1, q2: q2, q3: q3}; - }; + if((typeof p[0])!="object"){ + for(var i=0;i< p.length;i++){ + p[i]=[p[i]]; + } - exports.pooledStandardDeviation = function pooledStandardDeviation(samples, unbiased) { - return Math.sqrt(exports.pooledVariance(samples, unbiased)); - }; + } + //p = p(:); y_dat = y_dat(:); // make column vectors + var i,k; + var eps = 2^-52; + var Npar = p.length;//length(p); // number of parameters + var Npnt = y_dat.length;//length(y_dat); // number of data points + var p_old = Matrix.zeros(Npar,1); // previous set of parameters + var y_old = Matrix.zeros(Npnt,1); // previous model, y_old = y_hat(t;p_old) + var X2 = 1e-2/eps; // a really big initial Chi-sq value + var X2_old = 1e-2/eps; // a really big initial Chi-sq value + var J = Matrix.zeros(Npnt,Npar); - exports.pooledVariance = function pooledVariance(samples, unbiased) { - if (typeof(unbiased) === 'undefined') unbiased = true; - var sum = 0; - var length = 0, l = samples.length; - for (var i = 0; i < l; i++) { - var values = samples[i]; - var vari = exports.variance(values); - sum += (values.length - 1) * vari; + if (t.length != y_dat.length) { + console.log('lm.m error: the length of t must equal the length of y_dat'); - if (unbiased) - length += values.length - 1; - else - length += values.length; - } - return sum / length; - }; + length_t = t.length; + length_y_dat = y_dat.length; + var X2 = 0, corr = 0, sigma_p = 0, sigma_y = 0, R_sq = 0, cvg_hist = 0; + if (!tensor_parameter) { + return; + } + } - exports.mode = function mode(values) { - var l = values.length, - itemCount = new Array(l), - i; - for (i = 0; i < l; i++) { - itemCount[i] = 0; - } - var itemArray = new Array(l); - var count = 0; + weight = weight||Math.sqrt((Npnt-Npar+1)/(math.multiply(math.transpose(y_dat),y_dat))); + dp = dp || 0.001; + p_min = p_min || math.multiply(Math.abs(p),-100); + p_max = p_max || math.multiply(Math.abs(p),100); + c = c || 1; + // Algorithmic Paramters + //prnt MaxIter eps1 eps2 epx3 eps4 lam0 lamUP lamDN UpdateType + opts = opts ||[ 3,10*Npar, 1e-3, 1e-3, 1e-3, 1e-2, 1e-2, 11, 9, 1 ]; - for (i = 0; i < l; i++) { - var index = itemArray.indexOf(values[i]); - if (index >= 0) - itemCount[index]++; - else { - itemArray[count] = values[i]; - itemCount[count] = 1; - count++; + var prnt = opts[0]; // >1 intermediate results; >2 plots + var MaxIter = opts[1]; // maximum number of iterations + var epsilon_1 = opts[2]; // convergence tolerance for gradient + var epsilon_2 = opts[3]; // convergence tolerance for parameter + var epsilon_3 = opts[4]; // convergence tolerance for Chi-square + var epsilon_4 = opts[5]; // determines acceptance of a L-M step + var lambda_0 = opts[6]; // initial value of damping paramter, lambda + var lambda_UP_fac = opts[7]; // factor for increasing lambda + var lambda_DN_fac = opts[8]; // factor for decreasing lambda + var Update_Type = opts[9]; // 1: Levenberg-Marquardt lambda update + // 2: Quadratic update + // 3: Nielsen's lambda update equations + + if ( tensor_parameter && prnt == 3 ) prnt = 2; + + + if(!dp.length || dp.length == 1){ + var dp_array = new Array(Npar); + for(var i=0;i maxValue) { - maxValue = itemCount[i]; - maxIndex = i; + // indices of the parameters to be fit + var idx = []; + for(i=0;i 2; + //this is a big step + // --- Are parameters [p+h] much better than [p] ? + var hidx = new Array(idx.length); + for(k=0;k epsilon_4 ) { // it IS significantly better + //console.log("Here"); + dX2 = X2 - X2_old; + X2_old = X2; + p_old = p; + y_old = y_hat; + p = p_try; // accept p_try + + result = this.lm_matx(func, t, p_old, y_old, dX2, J, p, y_dat, weight_sq, dp, c); + JtWJ = result.JtWJ,JtWdy=result.JtWdy,X2=result.Chi_sq,y_hat=result.y_hat,J=result.J; + // decrease lambda ==> Gauss-Newton method + + switch (Update_Type) { + case 1: // Levenberg + lambda = Math.max(lambda / lambda_DN_fac, 1.e-7); + break; + case 2: // Quadratic + lambda = Math.max(lambda / (1 + alpha), 1.e-7); + break; + case 3: // Nielsen + lambda = math.multiply(Math.max(1 / 3, 1 - (2 * rho - 1) ^ 3),lambda); + nu = 2; + break; + } + } + else { // it IS NOT better + X2 = X2_old; // do not accept p_try + if (iteration%(2 * Npar)==0) { // rank-1 update of Jacobian + result = this.lm_matx(func, t, p_old, y_old, -1, J, p, y_dat, weight_sq, dp, c); + JtWJ = result.JtWJ,JtWdy=result.JtWdy,dX2=result.Chi_sq,y_hat=result.y_hat,J=result.J; + } - exports.weightedVariance = function weightedVariance(values, weights) { - var theMean = exports.weightedMean(values, weights); - var vari = 0, l = values.length; - var a = 0, b = 0; + // increase lambda ==> gradient descent method + switch (Update_Type) { + case 1: // Levenberg + lambda = Math.min(lambda * lambda_UP_fac, 1.e7); + break; + case 2: // Quadratic + lambda = lambda + Math.abs((X2_try - X2) / 2 / alpha); + break; + case 3: // Nielsen + lambda = lambda * nu; + nu = 2 * nu; + break; + } + } + }// --- End of Main Loop - for (var i = 0; i < l; i++) { - var z = values[i] - theMean; - var w = weights[i]; + // --- convergence achieved, find covariance and confidence intervals - vari += w * (z * z); - b += w; - a += w * w; - } + // equal weights for paramter error analysis + weight_sq = math.multiply(math.multiply(math.transpose(delta_y),delta_y), Matrix.ones(Npnt,1)); - return vari * (b / (b * b - a)); - }; + weight_sq.apply(function(i,j){ + weight_sq[i][j] = (Npnt-Nfit+1)/weight_sq[i][j]; + }); + //console.log(weight_sq); + result = this.lm_matx(func,t,p_old,y_old,-1,J,p,y_dat,weight_sq,dp,c); + JtWJ = result.JtWJ,JtWdy=result.JtWdy,X2=result.Chi_sq,y_hat=result.y_hat,J=result.J; + + /*if nargout > 2 // standard error of parameters + covar = inv(JtWJ); + sigma_p = sqrt(diag(covar)); + end + + if nargout > 3 // standard error of the fit + // sigma_y = sqrt(diag(J * covar * J')); // slower version of below + sigma_y = zeros(Npnt,1); + for i=1:Npnt + sigma_y(i) = J(i,:) * covar * J(i,:)'; + end + sigma_y = sqrt(sigma_y); + end + + if nargout > 4 // parameter correlation matrix + corr = covar ./ [sigma_p*sigma_p']; + end + + if nargout > 5 // coefficient of multiple determination + R_sq = corrcoef([y_dat y_hat]); + R_sq = R_sq(1,2).^2; + end + + if nargout > 6 // convergence history + cvg_hst = cvg_hst(1:iteration,:); + end*/ + + // endfunction # ---------------------------------------------------------- LM + + return { p:p, X2:X2}; + }, - exports.center = function center(values, inPlace) { - if (typeof(inPlace) === 'undefined') inPlace = false; + lm_FD_J:function(func,t,p,y,dp,c) { + // J = lm_FD_J(func,t,p,y,{dp},{c}) + // + // partial derivatives (Jacobian) dy/dp for use with lm.m + // computed via Finite Differences + // Requires n or 2n function evaluations, n = number of nonzero values of dp + // -------- INPUT VARIABLES --------- + // func = function of independent variables, 't', and parameters, 'p', + // returning the simulated model: y_hat = func(t,p,c) + // t = m-vector of independent variables (used as arg to func) + // p = n-vector of current parameter values + // y = func(t,p,c) n-vector initialised by user before each call to lm_FD_J + // dp = fractional increment of p for numerical derivatives + // dp(j)>0 central differences calculated + // dp(j)<0 one sided differences calculated + // dp(j)=0 sets corresponding partials to zero; i.e. holds p(j) fixed + // Default: 0.001; + // c = optional vector of constants passed to y_hat = func(t,p,c) + //---------- OUTPUT VARIABLES ------- + // J = Jacobian Matrix J(i,j)=dy(i)/dp(j) i=1:n; j=1:m + + // Henri Gavin, Dept. Civil & Environ. Engineering, Duke Univ. November 2005 + // modified from: ftp://fly.cnuce.cnr.it/pub/software/octave/leasqr/ + // Press, et al., Numerical Recipes, Cambridge Univ. Press, 1992, Chapter 15. + + var m = y.length; // number of data points + var n = p.length; // number of parameters + + dp = dp || math.multiply( Matrix.ones(n, 1), 0.001); + + var ps = p.clone();//JSON.parse(JSON.stringify(p)); + //var ps = $.extend(true, [], p); + var J = new Matrix(m,n), del =new Array(n); // initialize Jacobian to Zero + + for (var j = 0;j < n; j++) { + //console.log(j+" "+dp[j]+" "+p[j]+" "+ps[j]+" "+del[j]); + del[j] = dp[j]*(1+Math.abs(p[j][0])); // parameter perturbation + p[j] = [ps[j][0]+del[j]]; // perturb parameter p(j) + //console.log(j+" "+dp[j]+" "+p[j]+" "+ps[j]+" "+del[j]); + + if (del[j] != 0){ + y1 = func(t, p, c); + //func_calls = func_calls + 1; + if (dp[j][0] < 0) { // backwards difference + //J(:,j) = math.dotDivide(math.subtract(y1, y),del[j]);//. / del[j]; + //console.log(del[j]); + //console.log(y); + var column = math.dotDivide(math.subtract(y1, y),del[j]); + for(var k=0;k< m;k++){ + J[k][j]=column[k][0]; + } + //console.log(column); + } + else{ + p[j][0] = ps[j][0] - del[j]; + //J(:,j) = (y1 - feval(func, t, p, c)). / (2. * del[j]); + var column = math.dotDivide(math.subtract(y1,func(t,p,c)),2*del[j]); + for(var k=0;k< m;k++){ + J[k][j]=column[k][0]; + } - var result = values; - if (!inPlace) - result = values.slice(); + } // central difference, additional func call + } + + p[j] = ps[j]; // restore p(j) + + } + //console.log("lm_FD_J: "+ JSON.stringify(J)); + return J; + + }, + + // endfunction # -------------------------------------------------- LM_FD_J + lm_Broyden_J: function(p_old,y_old,J,p,y){ + // J = lm_Broyden_J(p_old,y_old,J,p,y) + // carry out a rank-1 update to the Jacobian matrix using Broyden's equation + //---------- INPUT VARIABLES ------- + // p_old = previous set of parameters + // y_old = model evaluation at previous set of parameters, y_hat(t;p_old) + // J = current version of the Jacobian matrix + // p = current set of parameters + // y = model evaluation at current set of parameters, y_hat(t;p) + //---------- OUTPUT VARIABLES ------- + // J = rank-1 update to Jacobian Matrix J(i,j)=dy(i)/dp(j) i=1:n; j=1:m + //console.log(p+" X "+ p_old) + var h = math.subtract(p, p_old); + + //console.log("hhh "+h); + var h_t = math.transpose(h); + h_t.div(math.multiply(h_t,h)); + + //console.log(h_t); + //J = J + ( y - y_old - J*h )*h' / (h'*h); // Broyden rank-1 update eq'n + J = math.add(J, math.multiply(math.subtract(y, math.add(y_old,math.multiply(J,h))),h_t)); + return J; + // endfunction # ---------------------------------------------- LM_Broyden_J + }, + + lm_matx : function (func,t,p_old,y_old,dX2,J,p,y_dat,weight_sq,dp,c,iteration){ + // [JtWJ,JtWdy,Chi_sq,y_hat,J] = this.lm_matx(func,t,p_old,y_old,dX2,J,p,y_dat,weight_sq,{da},{c}) + // + // Evaluate the linearized fitting matrix, JtWJ, and vector JtWdy, + // and calculate the Chi-squared error function, Chi_sq + // Used by Levenberg-Marquard algorithm, lm.m + // -------- INPUT VARIABLES --------- + // func = function ofpn independent variables, p, and m parameters, p, + // returning the simulated model: y_hat = func(t,p,c) + // t = m-vectors or matrix of independent variables (used as arg to func) + // p_old = n-vector of previous parameter values + // y_old = m-vector of previous model ... y_old = y_hat(t;p_old); + // dX2 = previous change in Chi-squared criteria + // J = m-by-n Jacobian of model, y_hat, with respect to parameters, p + // p = n-vector of current parameter values + // y_dat = n-vector of data to be fit by func(t,p,c) + // weight_sq = square of the weighting vector for least squares fit ... + // inverse of the standard measurement errors + // dp = fractional increment of 'p' for numerical derivatives + // dp(j)>0 central differences calculated + // dp(j)<0 one sided differences calculated + // dp(j)=0 sets corresponding partials to zero; i.e. holds p(j) fixed + // Default: 0.001; + // c = optional vector of constants passed to y_hat = func(t,p,c) + //---------- OUTPUT VARIABLES ------- + // JtWJ = linearized Hessian matrix (inverse of covariance matrix) + // JtWdy = linearized fitting vector + // Chi_sq = Chi-squared criteria: weighted sum of the squared residuals WSSR + // y_hat = model evaluated with parameters 'p' + // J = m-by-n Jacobian of model, y_hat, with respect to parameters, p + + // Henri Gavin, Dept. Civil & Environ. Engineering, Duke Univ. November 2005 + // modified from: ftp://fly.cnuce.cnr.it/pub/software/octave/leasqr/ + // Press, et al., Numerical Recipes, Cambridge Univ. Press, 1992, Chapter 15. + + + var Npnt = y_dat.length; // number of data points + var Npar = p.length; // number of parameters + + dp = dp || 0.001; + + + //var JtWJ = new Matrix.zeros(Npar); + //var JtWdy = new Matrix.zeros(Npar,1); + + var y_hat = func(t,p,c); // evaluate model using parameters 'p' + //func_calls = func_calls + 1; + //console.log(J); + if ( (iteration%(2*Npar))==0 || dX2 > 0 ) { + //console.log("Par"); + J = this.lm_FD_J(func, t, p, y_hat, dp, c); // finite difference + } + else{ + //console.log("ImPar"); + J = this.lm_Broyden_J(p_old, y_old, J, p, y_hat); // rank-1 update + } + var delta_y = math.subtract(y_dat, y_hat); // residual error between model and data + //console.log(delta_y[0][0]); + //console.log(delta_y.rows+" "+delta_y.columns+" "+JSON.stringify(weight_sq)); + //var Chi_sq = delta_y' * ( delta_y .* weight_sq ); // Chi-squared error criteria + var Chi_sq = math.multiply(math.transpose(delta_y),math.dotMultiply(delta_y,weight_sq)); + //JtWJ = J' * ( J .* ( weight_sq * ones(1,Npar) ) ); + var Jt = math.transpose(J); + + //console.log(weight_sq); + + var JtWJ = math.multiply(Jt, math.dotMultiply(J,math.multiply(weight_sq, Matrix.ones(1,Npar)))); + + //JtWdy = J' * ( weight_sq .* delta_y ); + var JtWdy = math.multiply(Jt, math.dotMultiply(weight_sq,delta_y)); + + + return {JtWJ:JtWJ,JtWdy:JtWdy,Chi_sq:Chi_sq,y_hat:y_hat,J:J}; + // endfunction # ------------------------------------------------------ LM_MATX + } - var theMean = exports.mean(result), l = result.length; - for (var i = 0; i < l; i++) - result[i] -= theMean; - }; - exports.standardize = function standardize(values, standardDev, inPlace) { - if (typeof(standardDev) === 'undefined') standardDev = exports.standardDeviation(values); - if (typeof(inPlace) === 'undefined') inPlace = false; - var l = values.length; - var result = inPlace ? values : new Array(l); - for (var i = 0; i < l; i++) - result[i] = values[i] / standardDev; - return result; - }; - exports.cumulativeSum = function cumulativeSum(array) { - var l = array.length; - var result = new Array(l); - result[0] = array[0]; - for (var i = 1; i < l; i++) - result[i] = result[i - 1] + array[i]; - return result; }; + module.exports = LM; /***/ }, -/* 104 */ +/* 103 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; - var arrayStat = __webpack_require__(103); - // https://github.com/accord-net/framework/blob/development/Sources/Accord.Statistics/Tools.cs + module.exports = __webpack_require__(104); + module.exports.Decompositions = module.exports.DC = __webpack_require__(105); - function entropy(matrix, eps) { - if (typeof(eps) === 'undefined') { - eps = 0; - } - var sum = 0, - l1 = matrix.length, - l2 = matrix[0].length; - for (var i = 0; i < l1; i++) { - for (var j = 0; j < l2; j++) { - sum += matrix[i][j] * Math.log(matrix[i][j] + eps); - } - } - return -sum; - } - function mean(matrix, dimension) { - if (typeof(dimension) === 'undefined') { - dimension = 0; +/***/ }, +/* 104 */ +/***/ function(module, exports) { + + 'use strict'; + + var Asplice = Array.prototype.splice, + Aconcat = Array.prototype.concat; + + // For performance : http://jsperf.com/clone-array-slice-vs-while-vs-for + function slice(arr) { + var i = 0, + ii = arr.length, + result = new Array(ii); + for (; i < ii; i++) { + result[i] = arr[i]; } - var rows = matrix.length, - cols = matrix[0].length, - theMean, N, i, j; + return result; + } - if (dimension === -1) { - theMean = [0]; - N = rows * cols; - for (i = 0; i < rows; i++) { - for (j = 0; j < cols; j++) { - theMean[0] += matrix[i][j]; - } - } - theMean[0] /= N; - } else if (dimension === 0) { - theMean = new Array(cols); - N = rows; - for (j = 0; j < cols; j++) { - theMean[j] = 0; - for (i = 0; i < rows; i++) { - theMean[j] += matrix[i][j]; + /** + * Real matrix. + * @constructor + * @param {number|Array} nRows - Number of rows of the new matrix or a 2D array containing the data. + * @param {number|boolean} [nColumns] - Number of columns of the new matrix or a boolean specifying if the input array should be cloned + */ + function Matrix(nRows, nColumns) { + var i = 0, rows, columns, matrix, newInstance; + if (Array.isArray(nRows)) { + newInstance = nColumns; + matrix = newInstance ? slice(nRows) : nRows; + nRows = matrix.length; + nColumns = matrix[0].length; + if (typeof nColumns === 'undefined') { + throw new TypeError('Data must be a 2D array'); + } + if (nRows > 0 && nColumns > 0) { + for (; i < nRows; i++) { + if (matrix[i].length !== nColumns) { + throw new RangeError('Inconsistent array dimensions'); + } else if (newInstance) { + matrix[i] = slice(matrix[i]); + } } - theMean[j] /= N; + } else { + throw new RangeError('Invalid dimensions: ' + nRows + 'x' + nColumns); } - } else if (dimension === 1) { - theMean = new Array(rows); - N = cols; - for (j = 0; j < rows; j++) { - theMean[j] = 0; - for (i = 0; i < cols; i++) { - theMean[j] += matrix[j][i]; + } else if (typeof nRows === 'number') { // Create empty matrix + if (nRows > 0 && nColumns > 0) { + matrix = new Array(nRows); + for (; i < nRows; i++) { + matrix[i] = new Array(nColumns); } - theMean[j] /= N; + } else { + throw new RangeError('Invalid dimensions: ' + nRows + 'x' + nColumns); } } else { - throw new Error('Invalid dimension'); + throw new TypeError('Invalid arguments'); } - return theMean; - } - function standardDeviation(matrix, means, unbiased) { - var vari = variance(matrix, means, unbiased), l = vari.length; - for (var i = 0; i < l; i++) { - vari[i] = Math.sqrt(vari[i]); - } - return vari; + Object.defineProperty(matrix, 'rows', {writable: true, value: nRows}); + Object.defineProperty(matrix, 'columns', {writable: true, value: nColumns}); + + matrix.__proto__ = Matrix.prototype; + + return matrix; } - function variance(matrix, means, unbiased) { - if (typeof(unbiased) === 'undefined') { - unbiased = true; - } - means = means || mean(matrix); - var rows = matrix.length; - if (rows === 0) return []; - var cols = matrix[0].length; - var vari = new Array(cols); + /** + * Constructs a Matrix with the chosen dimensions from a 1D array. + * @param {number} newRows - Number of rows + * @param {number} newColumns - Number of columns + * @param {Array} newData - A 1D array containing data for the matrix + * @returns {Matrix} - The new matrix + */ + Matrix.from1DArray = function from1DArray(newRows, newColumns, newData) { + var length, data, i = 0; - for (var j = 0; j < cols; j++) { - var sum1 = 0, sum2 = 0, x = 0; - for (var i = 0; i < rows; i++) { - x = matrix[i][j] - means[j]; - sum1 += x; - sum2 += x * x; - } - if (unbiased) { - vari[j] = (sum2 - ((sum1 * sum1) / rows)) / (rows - 1); - } else { - vari[j] = (sum2 - ((sum1 * sum1) / rows)) / rows; - } + length = newRows * newColumns; + if (length !== newData.length) + throw new RangeError('Data length does not match given dimensions'); + + data = new Array(newRows); + for (; i < newRows; i++) { + data[i] = newData.slice(i * newColumns, (i + 1) * newColumns); } - return vari; - } + return new Matrix(data); + }; - function median(matrix) { - var rows = matrix.length, cols = matrix[0].length; - var medians = new Array(cols); + /** + * Creates a row vector, a matrix with only one row. + * @param {Array} newData - A 1D array containing data for the vector + * @returns {Matrix} - The new matrix + */ + Matrix.rowVector = function rowVector(newData) { + return new Matrix([newData]); + }; - for (var i = 0; i < cols; i++) { - var data = new Array(rows); - for (var j = 0; j < rows; j++) { - data[j] = matrix[j][i]; - } - data.sort(); - var N = data.length; - if (N % 2 === 0) { - medians[i] = (data[N / 2] + data[(N / 2) - 1]) * 0.5; - } else { - medians[i] = data[Math.floor(N / 2)]; + /** + * Creates a column vector, a matrix with only one column. + * @param {Array} newData - A 1D array containing data for the vector + * @returns {Matrix} - The new matrix + */ + Matrix.columnVector = function columnVector(newData) { + var l = newData.length, vector = new Array(l); + for (var i = 0; i < l; i++) + vector[i] = [newData[i]]; + return new Matrix(vector); + }; + + /** + * Creates an empty matrix with the given dimensions. Values will be undefined. Same as using new Matrix(rows, columns). + * @param {number} rows - Number of rows + * @param {number} columns - Number of columns + * @returns {Matrix} - The new matrix + */ + Matrix.empty = function empty(rows, columns) { + return new Matrix(rows, columns); + }; + + /** + * Creates a matrix with the given dimensions. Values will be set to zero. + * @param {number} rows - Number of rows + * @param {number} columns - Number of columns + * @returns {Matrix} - The new matrix + */ + Matrix.zeros = function zeros(rows, columns) { + return Matrix.empty(rows, columns).fill(0); + }; + + /** + * Creates a matrix with the given dimensions. Values will be set to one. + * @param {number} rows - Number of rows + * @param {number} columns - Number of columns + * @returns {Matrix} - The new matrix + */ + Matrix.ones = function ones(rows, columns) { + return Matrix.empty(rows, columns).fill(1); + }; + + /** + * Creates a matrix with the given dimensions. Values will be randomly set using Math.random(). + * @param {number} rows - Number of rows + * @param {number} columns - Number of columns + * @returns {Matrix} The new matrix + */ + Matrix.rand = function rand(rows, columns) { + var matrix = Matrix.empty(rows, columns); + for (var i = 0, ii = matrix.rows; i < ii; i++) { + for (var j = 0, jj = matrix.columns; j < jj; j++) { + matrix[i][j] = Math.random(); } } - return medians; - } + return matrix; + }; - function mode(matrix) { - var rows = matrix.length, - cols = matrix[0].length, - modes = new Array(cols), - i, j; - for (i = 0; i < cols; i++) { - var itemCount = new Array(rows); - for (var k = 0; k < rows; k++) { - itemCount[k] = 0; - } - var itemArray = new Array(rows); - var count = 0; + /** + * Creates an identity matrix with the given dimension. Values of the diagonal will be 1 and other will be 0. + * @param {number} n - Number of rows and columns + * @returns {Matrix} - The new matrix + */ + Matrix.eye = function eye(n) { + var matrix = Matrix.zeros(n, n), l = matrix.rows; + for (var i = 0; i < l; i++) { + matrix[i][i] = 1; + } + return matrix; + }; - for (j = 0; j < rows; j++) { - var index = itemArray.indexOf(matrix[j][i]); - if (index >= 0) { - itemCount[index]++; - } else { - itemArray[count] = matrix[j][i]; - itemCount[count] = 1; - count++; - } + /** + * Creates a diagonal matrix based on the given array. + * @param {Array} data - Array containing the data for the diagonal + * @returns {Matrix} - The new matrix + */ + Matrix.diag = function diag(data) { + var l = data.length, matrix = Matrix.zeros(l, l); + for (var i = 0; i < l; i++) { + matrix[i][i] = data[i]; + } + return matrix; + }; + + /** + * Creates an array of indices between two values + * @param {number} from + * @param {number} to + * @returns {Array} + */ + Matrix.indices = function indices(from, to) { + var vector = new Array(to - from); + for (var i = 0; i < vector.length; i++) + vector[i] = from++; + return vector; + }; + + // TODO DOC + Matrix.stack = function stack(arg1) { + var i, j, k; + if (Matrix.isMatrix(arg1)) { + var rows = 0, + cols = 0; + for (i = 0; i < arguments.length; i++) { + rows += arguments[i].rows; + if (arguments[i].columns > cols) + cols = arguments[i].columns; } - var maxValue = 0, maxIndex = 0; - for (j = 0; j < count; j++) { - if (itemCount[j] > maxValue) { - maxValue = itemCount[j]; - maxIndex = j; + var r = Matrix.zeros(rows, cols); + var c = 0; + for (i = 0; i < arguments.length; i++) { + var current = arguments[i]; + for (j = 0; j < current.rows; j++) { + for (k = 0; k < current.columns; k++) + r[c][k] = current[j][k]; + c++; } } - - modes[i] = itemArray[maxIndex]; + return r; } - return modes; - } + else if (Array.isArray(arg1)) { + var matrix = Matrix.empty(arguments.length, arg1.length); + for (i = 0; i < arguments.length; i++) + matrix.setRow(i, arguments[i]); + return matrix; + } + }; - function skewness(matrix, unbiased) { - if (typeof(unbiased) === 'undefined') unbiased = true; - var means = mean(matrix); - var n = matrix.length, l = means.length; - var skew = new Array(l); + // TODO DOC + Matrix.expand = function expand(base, count) { + var expansion = []; + for (var i = 0; i < count.length; i++) + for (var j = 0; j < count[i]; j++) + expansion.push(base[i]); + return new Matrix(expansion); + }; - for (var j = 0; j < l; j++) { - var s2 = 0, s3 = 0; - for (var i = 0; i < n; i++) { - var dev = matrix[i][j] - means[j]; - s2 += dev * dev; - s3 += dev * dev * dev; - } + /** + * Check that the provided value is a Matrix and tries to instantiate one if not + * @param value - The value to check + * @returns {Matrix} + * @throws {TypeError} + */ + Matrix.checkMatrix = function checkMatrix(value) { + if (!value) { + throw new TypeError('Argument has to be a matrix'); + } + if (value.klass !== 'Matrix') { + value = new Matrix(value); + } + return value; + }; - var m2 = s2 / n; - var m3 = s3 / n; - var g = m3 / Math.pow(m2, 3 / 2); + /** + * Returns true if the argument is a Matrix, false otherwise + * @param value - The value to check + * @returns {boolean} + */ + Matrix.isMatrix = function isMatrix(value) { + return value ? value.klass === 'Matrix' : false; + }; - if (unbiased) { - var a = Math.sqrt(n * (n - 1)); - var b = n - 2; - skew[j] = (a / b) * g; - } else { - skew[j] = g; - } + /** + * @property {string} - The name of this class. + */ + Object.defineProperty(Matrix.prototype, 'klass', { + get: function klass() { + return 'Matrix'; } - return skew; - } + }); - function kurtosis(matrix, unbiased) { - if (typeof(unbiased) === 'undefined') unbiased = true; - var means = mean(matrix); - var n = matrix.length, m = matrix[0].length; - var kurt = new Array(m); + /** + * @property {number} - The number of elements in the matrix. + */ + Object.defineProperty(Matrix.prototype, 'size', { + get: function size() { + return this.rows * this.columns; + } + }); - for (var j = 0; j < m; j++) { - var s2 = 0, s4 = 0; - for (var i = 0; i < n; i++) { - var dev = matrix[i][j] - means[j]; - s2 += dev * dev; - s4 += dev * dev * dev * dev; - } - var m2 = s2 / n; - var m4 = s4 / n; + /** + * @private + * Internal check that a row index is not out of bounds + * @param {number} index + */ + Matrix.prototype.checkRowIndex = function checkRowIndex(index) { + if (index < 0 || index > this.rows - 1) + throw new RangeError('Row index out of range.'); + }; - if (unbiased) { - var v = s2 / (n - 1); - var a = (n * (n + 1)) / ((n - 1) * (n - 2) * (n - 3)); - var b = s4 / (v * v); - var c = ((n - 1) * (n - 1)) / ((n - 2) * (n - 3)); - kurt[j] = a * b - 3 * c; - } else { - kurt[j] = m4 / (m2 * m2) - 3; + /** + * @private + * Internal check that a column index is not out of bounds + * @param {number} index + */ + Matrix.prototype.checkColumnIndex = function checkColumnIndex(index) { + if (index < 0 || index > this.columns - 1) + throw new RangeError('Column index out of range.'); + }; + + /** + * @private + * Internal check that two matrices have the same dimensions + * @param {Matrix} otherMatrix + */ + Matrix.prototype.checkDimensions = function checkDimensions(otherMatrix) { + if ((this.rows !== otherMatrix.rows) || (this.columns !== otherMatrix.columns)) + throw new RangeError('Matrices dimensions must be equal.'); + }; + + /** + * Applies a callback for each element of the matrix. The function is called in the matrix (this) context. + * @param {function} callback - Function that will be called with two parameters : i (row) and j (column) + * @returns {Matrix} this + */ + Matrix.prototype.apply = function apply(callback) { + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + callback.call(this, i, j); } } - return kurt; - } + return this; + }; - function standardError(matrix) { - var samples = matrix.length; - var standardDeviations = standardDeviation(matrix), l = standardDeviations.length; - var standardErrors = new Array(l); - var sqrtN = Math.sqrt(samples); + /** + * Creates an exact and independent copy of the matrix + * @returns {Matrix} + */ + Matrix.prototype.clone = function clone() { + return new Matrix(this.to2DArray()); + }; + + /** + * Returns a new 1D array filled row by row with the matrix values + * @returns {Array} + */ + Matrix.prototype.to1DArray = function to1DArray() { + return Aconcat.apply([], this); + }; + /** + * Returns a 2D array containing a copy of the data + * @returns {Array} + */ + Matrix.prototype.to2DArray = function to2DArray() { + var l = this.rows, copy = new Array(l); for (var i = 0; i < l; i++) { - standardErrors[i] = standardDeviations[i] / sqrtN; + copy[i] = slice(this[i]); } - return standardErrors; - } + return copy; + }; - function covariance(matrix, dimension) { - return scatter(matrix, undefined, dimension); - } + /** + * @returns {boolean} true if the matrix has one row + */ + Matrix.prototype.isRowVector = function isRowVector() { + return this.rows === 1; + }; - function scatter(matrix, divisor, dimension) { - if (typeof(dimension) === 'undefined') { - dimension = 0; - } - if (typeof(divisor) === 'undefined') { - if (dimension === 0) { - divisor = matrix.length - 1; - } else if (dimension === 1) { - divisor = matrix[0].length - 1; - } - } - var means = mean(matrix, dimension), - rows = matrix.length; - if (rows === 0) { - return [[]]; - } - var cols = matrix[0].length, - cov, i, j, s, k; + /** + * @returns {boolean} true if the matrix has one column + */ + Matrix.prototype.isColumnVector = function isColumnVector() { + return this.columns === 1; + }; - if (dimension === 0) { - cov = new Array(cols); - for (i = 0; i < cols; i++) { - cov[i] = new Array(cols); - } - for (i = 0; i < cols; i++) { - for (j = i; j < cols; j++) { - s = 0; - for (k = 0; k < rows; k++) { - s += (matrix[k][j] - means[j]) * (matrix[k][i] - means[i]); + /** + * @returns {boolean} true if the matrix has one row or one column + */ + Matrix.prototype.isVector = function isVector() { + return (this.rows === 1) || (this.columns === 1); + }; + + /** + * @returns {boolean} true if the matrix has the same number of rows and columns + */ + Matrix.prototype.isSquare = function isSquare() { + return this.rows === this.columns; + }; + + /** + * @returns {boolean} true if the matrix is square and has the same values on both sides of the diagonal + */ + Matrix.prototype.isSymmetric = function isSymmetric() { + if (this.isSquare()) { + var l = this.rows; + for (var i = 0; i < l; i++) { + for (var j = 0; j <= i; j++) { + if (this[i][j] !== this[j][i]) { + return false; } - s /= divisor; - cov[i][j] = s; - cov[j][i] = s; } } - } else if (dimension === 1) { - cov = new Array(rows); - for (i = 0; i < rows; i++) { - cov[i] = new Array(rows); - } - for (i = 0; i < rows; i++) { - for (j = i; j < rows; j++) { - s = 0; - for (k = 0; k < cols; k++) { - s += (matrix[j][k] - means[j]) * (matrix[i][k] - means[i]); - } - s /= divisor; - cov[i][j] = s; - cov[j][i] = s; - } - } - } else { - throw new Error('Invalid dimension'); + return true; } + return false; + }; - return cov; - } + /** + * Sets a given element of the matrix. mat.set(3,4,1) is equivalent to mat[3][4]=1 + * @param {number} rowIndex - Index of the row + * @param {number} columnIndex - Index of the column + * @param {number} value - The new value for the element + * @returns {Matrix} this + */ + Matrix.prototype.set = function set(rowIndex, columnIndex, value) { + this[rowIndex][columnIndex] = value; + return this; + }; - function correlation(matrix) { - var means = mean(matrix), - standardDeviations = standardDeviation(matrix, true, means), - scores = zScores(matrix, means, standardDeviations), - rows = matrix.length, - cols = matrix[0].length, - i, j; + /** + * Returns the given element of the matrix. mat.get(3,4) is equivalent to matrix[3][4] + * @param {number} rowIndex - Index of the row + * @param {number} columnIndex - Index of the column + * @returns {number} + */ + Matrix.prototype.get = function get(rowIndex, columnIndex) { + return this[rowIndex][columnIndex]; + }; - var cor = new Array(cols); - for (i = 0; i < cols; i++) { - cor[i] = new Array(cols); - } - for (i = 0; i < cols; i++) { - for (j = i; j < cols; j++) { - var c = 0; - for (var k = 0, l = scores.length; k < l; k++) { - c += scores[k][j] * scores[k][i]; - } - c /= rows - 1; - cor[i][j] = c; - cor[j][i] = c; + /** + * Fills the matrix with a given value. All elements will be set to this value. + * @param {number} value - New value + * @returns {Matrix} this + */ + Matrix.prototype.fill = function fill(value) { + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + this[i][j] = value; } } - return cor; - } + return this; + }; - function zScores(matrix, means, standardDeviations) { - means = means || mean(matrix); - if (typeof(standardDeviations) === 'undefined') standardDeviations = standardDeviation(matrix, true, means); - return standardize(center(matrix, means, false), standardDeviations, true); - } + /** + * Negates the matrix. All elements will be multiplied by (-1) + * @returns {Matrix} this + */ + Matrix.prototype.neg = function neg() { + return this.mulS(-1); + }; - function center(matrix, means, inPlace) { - means = means || mean(matrix); - var result = matrix, - l = matrix.length, - i, j, jj; + /** + * Adds a scalar or values from another matrix (in place) + * @param {number|Matrix} value + * @returns {Matrix} this + */ + Matrix.prototype.add = function add(value) { + if (typeof value === 'number') + return this.addS(value); + value = Matrix.checkMatrix(value); + return this.addM(value); + }; - if (!inPlace) { - result = new Array(l); - for (i = 0; i < l; i++) { - result[i] = new Array(matrix[i].length); + /** + * Adds a scalar to each element of the matrix + * @param {number} value + * @returns {Matrix} this + */ + Matrix.prototype.addS = function addS(value) { + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + this[i][j] += value; } } + return this; + }; - for (i = 0; i < l; i++) { - var row = result[i]; - for (j = 0, jj = row.length; j < jj; j++) { - row[j] = matrix[i][j] - means[j]; + /** + * Adds the value of each element of matrix to the corresponding element of this + * @param {Matrix} matrix + * @returns {Matrix} this + */ + Matrix.prototype.addM = function addM(matrix) { + this.checkDimensions(matrix); + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + this[i][j] += matrix[i][j]; } } - return result; - } + return this; + }; - function standardize(matrix, standardDeviations, inPlace) { - if (typeof(standardDeviations) === 'undefined') standardDeviations = standardDeviation(matrix); - var result = matrix, - l = matrix.length, - i, j, jj; + /** + * Subtracts a scalar or values from another matrix (in place) + * @param {number|Matrix} value + * @returns {Matrix} this + */ + Matrix.prototype.sub = function sub(value) { + if (typeof value === 'number') + return this.subS(value); + value = Matrix.checkMatrix(value); + return this.subM(value); + }; - if (!inPlace) { - result = new Array(l); - for (i = 0; i < l; i++) { - result[i] = new Array(matrix[i].length); + /** + * Subtracts a scalar from each element of the matrix + * @param {number} value + * @returns {Matrix} this + */ + Matrix.prototype.subS = function subS(value) { + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + this[i][j] -= value; } } + return this; + }; - for (i = 0; i < l; i++) { - var resultRow = result[i]; - var sourceRow = matrix[i]; - for (j = 0, jj = resultRow.length; j < jj; j++) { - if (standardDeviations[j] !== 0 && !isNaN(standardDeviations[j])) { - resultRow[j] = sourceRow[j] / standardDeviations[j]; - } + /** + * Subtracts the value of each element of matrix from the corresponding element of this + * @param {Matrix} matrix + * @returns {Matrix} this + */ + Matrix.prototype.subM = function subM(matrix) { + this.checkDimensions(matrix); + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + this[i][j] -= matrix[i][j]; } } - return result; - } - - function weightedVariance(matrix, weights) { - var means = mean(matrix); - var rows = matrix.length; - if (rows === 0) return []; - var cols = matrix[0].length; - var vari = new Array(cols); - - for (var j = 0; j < cols; j++) { - var sum = 0; - var a = 0, b = 0; + return this; + }; - for (var i = 0; i < rows; i++) { - var z = matrix[i][j] - means[j]; - var w = weights[i]; + /** + * Multiplies a scalar or values from another matrix (in place) + * @param {number|Matrix} value + * @returns {Matrix} this + */ + Matrix.prototype.mul = function mul(value) { + if (typeof value === 'number') + return this.mulS(value); + value = Matrix.checkMatrix(value); + return this.mulM(value); + }; - sum += w * (z * z); - b += w; - a += w * w; + /** + * Multiplies a scalar with each element of the matrix + * @param {number} value + * @returns {Matrix} this + */ + Matrix.prototype.mulS = function mulS(value) { + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + this[i][j] *= value; } - - vari[j] = sum * (b / (b * b - a)); } + return this; + }; - return vari; - } - - function weightedMean(matrix, weights, dimension) { - if (typeof(dimension) === 'undefined') { - dimension = 0; + /** + * Multiplies the value of each element of matrix with the corresponding element of this + * @param {Matrix} matrix + * @returns {Matrix} this + */ + Matrix.prototype.mulM = function mulM(matrix) { + this.checkDimensions(matrix); + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + this[i][j] *= matrix[i][j]; + } } - var rows = matrix.length; - if (rows === 0) return []; - var cols = matrix[0].length, - means, i, ii, j, w, row; + return this; + }; - if (dimension === 0) { - means = new Array(cols); - for (i = 0; i < cols; i++) { - means[i] = 0; - } - for (i = 0; i < rows; i++) { - row = matrix[i]; - w = weights[i]; - for (j = 0; j < cols; j++) { - means[j] += row[j] * w; - } - } - } else if (dimension === 1) { - means = new Array(rows); - for (i = 0; i < rows; i++) { - means[i] = 0; - } - for (j = 0; j < rows; j++) { - row = matrix[j]; - w = weights[j]; - for (i = 0; i < cols; i++) { - means[j] += row[i] * w; - } + /** + * Divides by a scalar or values from another matrix (in place) + * @param {number|Matrix} value + * @returns {Matrix} this + */ + Matrix.prototype.div = function div(value) { + if (typeof value === 'number') + return this.divS(value); + value = Matrix.checkMatrix(value); + return this.divM(value); + }; + + /** + * Divides each element of the matrix by a scalar + * @param {number} value + * @returns {Matrix} this + */ + Matrix.prototype.divS = function divS(value) { + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + this[i][j] /= value; } - } else { - throw new Error('Invalid dimension'); } + return this; + }; - var weightSum = arrayStat.sum(weights); - if (weightSum !== 0) { - for (i = 0, ii = means.length; i < ii; i++) { - means[i] /= weightSum; + /** + * Divides each element of this by the corresponding element of matrix + * @param {Matrix} matrix + * @returns {Matrix} this + */ + Matrix.prototype.divM = function divM(matrix) { + this.checkDimensions(matrix); + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + this[i][j] /= matrix[i][j]; } } - return means; - } + return this; + }; - function weightedCovariance(matrix, weights, means, dimension) { - dimension = dimension || 0; - means = means || weightedMean(matrix, weights, dimension); - var s1 = 0, s2 = 0; - for (var i = 0, ii = weights.length; i < ii; i++) { - s1 += weights[i]; - s2 += weights[i] * weights[i]; - } - var factor = s1 / (s1 * s1 - s2); - return weightedScatter(matrix, weights, means, factor, dimension); - } + /** + * Returns a new array from the given row index + * @param {number} index - Row index + * @returns {Array} + */ + Matrix.prototype.getRow = function getRow(index) { + this.checkRowIndex(index); + return slice(this[index]); + }; - function weightedScatter(matrix, weights, means, factor, dimension) { - dimension = dimension || 0; - means = means || weightedMean(matrix, weights, dimension); - if (typeof(factor) === 'undefined') { - factor = 1; - } - var rows = matrix.length; - if (rows === 0) { - return [[]]; + /** + * Returns a new row vector from the given row index + * @param {number} index - Row index + * @returns {Matrix} + */ + Matrix.prototype.getRowVector = function getRowVector(index) { + return Matrix.rowVector(this.getRow(index)); + }; + + /** + * Sets a row at the given index + * @param {number} index - Row index + * @param {Array|Matrix} array - Array or vector + * @returns {Matrix} this + */ + Matrix.prototype.setRow = function setRow(index, array) { + this.checkRowIndex(index); + if (Matrix.isMatrix(array)) array = array.to1DArray(); + if (array.length !== this.columns) + throw new RangeError('Invalid row size'); + this[index] = slice(array); + return this; + }; + + /** + * Removes a row from the given index + * @param {number} index - Row index + * @returns {Matrix} this + */ + Matrix.prototype.removeRow = function removeRow(index) { + this.checkRowIndex(index); + if (this.rows === 1) + throw new RangeError('A matrix cannot have less than one row'); + Asplice.call(this, index, 1); + this.rows -= 1; + return this; + }; + + /** + * Adds a row at the given index + * @param {number} [index = this.rows] - Row index + * @param {Array|Matrix} array - Array or vector + * @returns {Matrix} this + */ + Matrix.prototype.addRow = function addRow(index, array) { + if (typeof array === 'undefined') { + array = index; + index = this.rows; + } + if (index < 0 || index > this.rows) + throw new RangeError('Row index out of range.'); + if (Matrix.isMatrix(array)) array = array.to1DArray(); + if (array.length !== this.columns) + throw new RangeError('Invalid row size'); + Asplice.call(this, index, 0, slice(array)); + this.rows += 1; + return this; + }; + + /** + * Swaps two rows + * @param {number} row1 - First row index + * @param {number} row2 - Second row index + * @returns {Matrix} this + */ + Matrix.prototype.swapRows = function swapRows(row1, row2) { + this.checkRowIndex(row1); + this.checkRowIndex(row2); + var temp = this[row1]; + this[row1] = this[row2]; + this[row2] = temp; + return this; + }; + + /** + * Returns a new array from the given column index + * @param {number} index - Column index + * @returns {Array} + */ + Matrix.prototype.getColumn = function getColumn(index) { + this.checkColumnIndex(index); + var l = this.rows, column = new Array(l); + for (var i = 0; i < l; i++) { + column[i] = this[i][index]; } - var cols = matrix[0].length, - cov, i, j, k, s; + return column; + }; - if (dimension === 0) { - cov = new Array(cols); - for (i = 0; i < cols; i++) { - cov[i] = new Array(cols); - } - for (i = 0; i < cols; i++) { - for (j = i; j < cols; j++) { - s = 0; - for (k = 0; k < rows; k++) { - s += weights[k] * (matrix[k][j] - means[j]) * (matrix[k][i] - means[i]); - } - cov[i][j] = s * factor; - cov[j][i] = s * factor; - } - } - } else if (dimension === 1) { - cov = new Array(rows); - for (i = 0; i < rows; i++) { - cov[i] = new Array(rows); - } - for (i = 0; i < rows; i++) { - for (j = i; j < rows; j++) { - s = 0; - for (k = 0; k < cols; k++) { - s += weights[k] * (matrix[j][k] - means[j]) * (matrix[i][k] - means[i]); - } - cov[i][j] = s * factor; - cov[j][i] = s * factor; - } - } - } else { - throw new Error('Invalid dimension'); + /** + * Returns a new column vector from the given column index + * @param {number} index - Column index + * @returns {Matrix} + */ + Matrix.prototype.getColumnVector = function getColumnVector(index) { + return Matrix.columnVector(this.getColumn(index)); + }; + + /** + * Sets a column at the given index + * @param {number} index - Column index + * @param {Array|Matrix} array - Array or vector + * @returns {Matrix} this + */ + Matrix.prototype.setColumn = function setColumn(index, array) { + this.checkColumnIndex(index); + if (Matrix.isMatrix(array)) array = array.to1DArray(); + var l = this.rows; + if (array.length !== l) + throw new RangeError('Invalid column size'); + for (var i = 0; i < l; i++) { + this[i][index] = array[i]; } + return this; + }; - return cov; - } + /** + * Removes a column from the given index + * @param {number} index - Column index + * @returns {Matrix} this + */ + Matrix.prototype.removeColumn = function removeColumn(index) { + this.checkColumnIndex(index); + if (this.columns === 1) + throw new RangeError('A matrix cannot have less than one column'); + for (var i = 0, ii = this.rows; i < ii; i++) { + this[i].splice(index, 1); + } + this.columns -= 1; + return this; + }; - module.exports = { - entropy: entropy, - mean: mean, - standardDeviation: standardDeviation, - variance: variance, - median: median, - mode: mode, - skewness: skewness, - kurtosis: kurtosis, - standardError: standardError, - covariance: covariance, - scatter: scatter, - correlation: correlation, - zScores: zScores, - center: center, - standardize: standardize, - weightedVariance: weightedVariance, - weightedMean: weightedMean, - weightedCovariance: weightedCovariance, - weightedScatter: weightedScatter + /** + * Adds a column at the given index + * @param {number} [index = this.columns] - Column index + * @param {Array|Matrix} array - Array or vector + * @returns {Matrix} this + */ + Matrix.prototype.addColumn = function addColumn(index, array) { + if (typeof array === 'undefined') { + array = index; + index = this.columns; + } + if (index < 0 || index > this.columns) + throw new RangeError('Column index out of range.'); + if (Matrix.isMatrix(array)) array = array.to1DArray(); + var l = this.rows; + if (array.length !== l) + throw new RangeError('Invalid column size'); + for (var i = 0; i < l; i++) { + this[i].splice(index, 0, array[i]); + } + this.columns += 1; + return this; }; + /** + * Swaps two columns + * @param {number} column1 - First column index + * @param {number} column2 - Second column index + * @returns {Matrix} this + */ + Matrix.prototype.swapColumns = function swapColumns(column1, column2) { + this.checkRowIndex(column1); + this.checkRowIndex(column2); + var l = this.rows, temp, row; + for (var i = 0; i < l; i++) { + row = this[i]; + temp = row[column1]; + row[column1] = row[column2]; + row[column2] = temp; + } + return this; + }; -/***/ }, -/* 105 */ -/***/ function(module, exports, __webpack_require__) { + /** + * @private + * Internal check that the provided vector is an array with the right length + * @param {Array|Matrix} vector + * @returns {Array} + * @throws {RangeError} + */ + Matrix.prototype.checkRowVector = function checkRowVector(vector) { + if (Matrix.isMatrix(vector)) + vector = vector.to1DArray(); + if (vector.length !== this.columns) + throw new RangeError('vector size must be the same as the number of columns'); + return vector; + }; - 'use strict'; + /** + * @private + * Internal check that the provided vector is an array with the right length + * @param {Array|Matrix} vector + * @returns {Array} + * @throws {RangeError} + */ + Matrix.prototype.checkColumnVector = function checkColumnVector(vector) { + if (Matrix.isMatrix(vector)) + vector = vector.to1DArray(); + if (vector.length !== this.rows) + throw new RangeError('vector size must be the same as the number of rows'); + return vector; + }; - const measures = __webpack_require__(106); + /** + * Adds the values of a vector to each row + * @param {Array|Matrix} vector - Array or vector + * @returns {Matrix} this + */ + Matrix.prototype.addRowVector = function addRowVector(vector) { + vector = this.checkRowVector(vector); + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + this[i][j] += vector[j]; + } + } + return this; + }; - class Performance { - /** - * - * @param prediction - The prediction matrix - * @param target - The target matrix (values: truthy for same class, falsy for different class) - * @param options - * - * @option all True if the entire matrix must be used. False to ignore the diagonal and lower part (default is false, for similarity/distance matrices) - * @option max True if the max value corresponds to a perfect match (like in similarity matrices), false if it is the min value (default is false, like in distance matrices. All values will be multiplied by -1) - */ - constructor(prediction, target, options) { - options = options || {}; - if (prediction.length !== target.length || prediction[0].length !== target[0].length) { - throw new Error('dimensions of prediction and target do not match'); + /** + * Subtracts the values of a vector from each row + * @param {Array|Matrix} vector - Array or vector + * @returns {Matrix} this + */ + Matrix.prototype.subRowVector = function subRowVector(vector) { + vector = this.checkRowVector(vector); + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + this[i][j] -= vector[j]; } - const rows = prediction.length; - const columns = prediction[0].length; - const neg = !options.max; + } + return this; + }; - const predP = []; + /** + * Multiplies the values of a vector with each row + * @param {Array|Matrix} vector - Array or vector + * @returns {Matrix} this + */ + Matrix.prototype.mulRowVector = function mulRowVector(vector) { + vector = this.checkRowVector(vector); + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + this[i][j] *= vector[j]; + } + } + return this; + }; - if (options.all) { - for (var i = 0; i < rows; i++) { - for (var j = 0; j < columns; j++) { - predP.push({ - pred: neg ? 0 - prediction[i][j] : prediction[i][j], - targ: target[i][j] - }); - } - } - } else { - if (rows < 3 || rows !== columns) { - throw new Error('When "all" option is false, the prediction matrix must be square and have at least 3 columns'); - } - for (var i = 0; i < rows - 1; i++) { - for (var j = i + 1; j < columns; j++) { - predP.push({ - pred: neg ? 0 - prediction[i][j] : prediction[i][j], - targ: target[i][j] - }); - } - } + /** + * Divides the values of each row by those of a vector + * @param {Array|Matrix} vector - Array or vector + * @returns {Matrix} this + */ + Matrix.prototype.divRowVector = function divRowVector(vector) { + vector = this.checkRowVector(vector); + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + this[i][j] /= vector[j]; } + } + return this; + }; - predP.sort((a, b) => b.pred - a.pred); + /** + * Adds the values of a vector to each column + * @param {Array|Matrix} vector - Array or vector + * @returns {Matrix} this + */ + Matrix.prototype.addColumnVector = function addColumnVector(vector) { + vector = this.checkColumnVector(vector); + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + this[i][j] += vector[i]; + } + } + return this; + }; - const cutoffs = this.cutoffs = [Number.MAX_VALUE]; - const fp = this.fp = [0]; - const tp = this.tp = [0]; + /** + * Subtracts the values of a vector from each column + * @param {Array|Matrix} vector - Array or vector + * @returns {Matrix} this + */ + Matrix.prototype.subColumnVector = function subColumnVector(vector) { + vector = this.checkColumnVector(vector); + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + this[i][j] -= vector[i]; + } + } + return this; + }; - var nPos = 0; - var nNeg = 0; + /** + * Multiplies the values of a vector with each column + * @param {Array|Matrix} vector - Array or vector + * @returns {Matrix} this + */ + Matrix.prototype.mulColumnVector = function mulColumnVector(vector) { + vector = this.checkColumnVector(vector); + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + this[i][j] *= vector[i]; + } + } + return this; + }; - var currentPred = predP[0].pred; - var nTp = 0; - var nFp = 0; - for (var i = 0; i < predP.length; i++) { - if (predP[i].pred !== currentPred) { - cutoffs.push(currentPred); - fp.push(nFp); - tp.push(nTp); - currentPred = predP[i].pred; - } - if (predP[i].targ) { - nPos++; - nTp++; - } else { - nNeg++; - nFp++; - } + /** + * Divides the values of each column by those of a vector + * @param {Array|Matrix} vector - Array or vector + * @returns {Matrix} this + */ + Matrix.prototype.divColumnVector = function divColumnVector(vector) { + vector = this.checkColumnVector(vector); + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + this[i][j] /= vector[i]; } - cutoffs.push(currentPred); - fp.push(nFp); - tp.push(nTp); + } + return this; + }; - const l = cutoffs.length; - const fn = this.fn = new Array(l); - const tn = this.tn = new Array(l); - const nPosPred = this.nPosPred = new Array(l); - const nNegPred = this.nNegPred = new Array(l); + /** + * Multiplies the values of a row with a scalar + * @param {number} index - Row index + * @param {number} value + * @returns {Matrix} this + */ + Matrix.prototype.mulRow = function mulRow(index, value) { + this.checkRowIndex(index); + var i = 0, l = this.columns; + for (; i < l; i++) { + this[index][i] *= value; + } + return this; + }; - for (var i = 0; i < l; i++) { - fn[i] = nPos - tp[i]; - tn[i] = nNeg - fp[i]; + /** + * Multiplies the values of a column with a scalar + * @param {number} index - Column index + * @param {number} value + * @returns {Matrix} this + */ + Matrix.prototype.mulColumn = function mulColumn(index, value) { + this.checkColumnIndex(index); + var i = 0, l = this.rows; + for (; i < l; i++) { + this[i][index] *= value; + } + }; - nPosPred[i] = tp[i] + fp[i]; - nNegPred[i] = tn[i] + fn[i]; + /** + * A matrix index + * @typedef {Object} MatrixIndex + * @property {number} row + * @property {number} column + */ + + /** + * Returns the maximum value of the matrix + * @returns {number} + */ + Matrix.prototype.max = function max() { + var v = -Infinity; + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + if (this[i][j] > v) { + v = this[i][j]; + } } + } + return v; + }; - this.nPos = nPos; - this.nNeg = nNeg; - this.nSamples = nPos + nNeg; + /** + * Returns the index of the maximum value + * @returns {MatrixIndex} + */ + Matrix.prototype.maxIndex = function maxIndex() { + var v = -Infinity; + var idx = {}; + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + if (this[i][j] > v) { + v = this[i][j]; + idx.row = i; + idx.column = j; + } + } } + return idx; + }; - /** - * Computes a measure from the prediction object. - * - * Many measures are available and can be combined : - * To create a ROC curve, you need fpr and tpr - * To create a DET curve, you need fnr and fpr - * To create a Lift chart, you need rpp and lift - * - * Possible measures are : threshold (Threshold), acc (Accuracy), err (Error rate), - * fpr (False positive rate), tpr (True positive rate), fnr (False negative rate), tnr (True negative rate), ppv (Positive predictive value), - * npv (Negative predictive value), pcfall (Prediction-conditioned fallout), pcmiss (Prediction-conditioned miss), lift (Lift value), rpp (Rate of positive predictions), rnp (Rate of negative predictions) - * - * @param measure - The short name of the measure - * - * @return [number] - */ - getMeasure(measure) { - if (typeof measure !== 'string') { - throw new Error('No measure specified'); - } - if (!measures[measure]) { - throw new Error(`The specified measure (${measure}) does not exist`); + /** + * Returns the minimum value of the matrix + * @returns {number} + */ + Matrix.prototype.min = function min() { + var v = Infinity; + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + if (this[i][j] < v) { + v = this[i][j]; + } } - return measures[measure](this); } + return v; + }; - /** - * Returns the area under the ROC curve - */ - getAURC() { - const l = this.cutoffs.length; - const x = new Array(l); - const y = new Array(l); - for (var i = 0; i < l; i++) { - x[i] = this.fp[i] / this.nNeg; - y[i] = this.tp[i] / this.nPos; + /** + * Returns the index of the minimum value + * @returns {MatrixIndex} + */ + Matrix.prototype.minIndex = function minIndex() { + var v = Infinity; + var idx = {}; + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + if (this[i][j] < v) { + v = this[i][j]; + idx.row = i; + idx.column = j; + } } - var auc = 0; - for (i = 1; i < l; i++) { - auc += 0.5 * (x[i] - x[i - 1]) * (y[i] + y[i - 1]); + } + return idx; + }; + + /** + * Returns the maximum value of one row + * @param {number} index - Row index + * @returns {number} + */ + Matrix.prototype.maxRow = function maxRow(index) { + this.checkRowIndex(index); + var v = -Infinity; + for (var i = 0, ii = this.columns; i < ii; i++) { + if (this[index][i] > v) { + v = this[index][i]; } - return auc; } + return v; + }; - /** - * Returns the area under the DET curve - */ - getAUDC() { - const l = this.cutoffs.length; - const x = new Array(l); - const y = new Array(l); - for (var i = 0; i < l; i++) { - x[i] = this.fn[i] / this.nPos; - y[i] = this.fp[i] / this.nNeg; + /** + * Returns the index of the maximum value of one row + * @param {number} index - Row index + * @returns {MatrixIndex} + */ + Matrix.prototype.maxRowIndex = function maxRowIndex(index) { + this.checkRowIndex(index); + var v = -Infinity; + var idx = { + row: index + }; + for (var i = 0, ii = this.columns; i < ii; i++) { + if (this[index][i] > v) { + v = this[index][i]; + idx.column = i; } - var auc = 0; - for (i = 1; i < l; i++) { - auc += 0.5 * (x[i] + x[i - 1]) * (y[i] - y[i - 1]); + } + return idx; + }; + + /** + * Returns the minimum value of one row + * @param {number} index - Row index + * @returns {number} + */ + Matrix.prototype.minRow = function minRow(index) { + this.checkRowIndex(index); + var v = Infinity; + for (var i = 0, ii = this.columns; i < ii; i++) { + if (this[index][i] < v) { + v = this[index][i]; } - return auc; } + return v; + }; - getDistribution(options) { - options = options || {}; - var cutLength = this.cutoffs.length; - var cutLow = options.xMin || Math.floor(this.cutoffs[cutLength - 1] * 100) / 100; - var cutHigh = options.xMax || Math.ceil(this.cutoffs[1] * 100) / 100; - var interval = options.interval || Math.floor(((cutHigh - cutLow) / 20 * 10000000) - 1) / 10000000; // Trick to avoid the precision problem of float numbers + /** + * Returns the index of the maximum value of one row + * @param {number} index - Row index + * @returns {MatrixIndex} + */ + Matrix.prototype.minRowIndex = function minRowIndex(index) { + this.checkRowIndex(index); + var v = Infinity; + var idx = { + row: index, + column: 0 + }; + for (var i = 0, ii = this.columns; i < ii; i++) { + if (this[index][i] < v) { + v = this[index][i]; + idx.column = i; + } + } + return idx; + }; - var xLabels = []; - var interValues = []; - var intraValues = []; - var interCumPercent = []; - var intraCumPercent = []; + /** + * Returns the maximum value of one column + * @param {number} index - Column index + * @returns {number} + */ + Matrix.prototype.maxColumn = function maxColumn(index) { + this.checkColumnIndex(index); + var v = -Infinity; + for (var i = 0, ii = this.rows; i < ii; i++) { + if (this[i][index] > v) { + v = this[i][index]; + } + } + return v; + }; - var nTP = this.tp[cutLength - 1], currentTP = 0; - var nFP = this.fp[cutLength - 1], currentFP = 0; + /** + * Returns the index of the maximum value of one column + * @param {number} index - Column index + * @returns {MatrixIndex} + */ + Matrix.prototype.maxColumnIndex = function maxColumnIndex(index) { + this.checkColumnIndex(index); + var v = -Infinity; + var idx = { + row: 0, + column: index + }; + for (var i = 0, ii = this.rows; i < ii; i++) { + if (this[i][index] > v) { + v = this[i][index]; + idx.row = i; + } + } + return idx; + }; - for (var i = cutLow, j = (cutLength - 1); i <= cutHigh; i += interval) { - while (this.cutoffs[j] < i) - j--; + /** + * Returns the minimum value of one column + * @param {number} index - Column index + * @returns {number} + */ + Matrix.prototype.minColumn = function minColumn(index) { + this.checkColumnIndex(index); + var v = Infinity; + for (var i = 0, ii = this.rows; i < ii; i++) { + if (this[i][index] < v) { + v = this[i][index]; + } + } + return v; + }; - xLabels.push(i); + /** + * Returns the index of the minimum value of one column + * @param {number} index - Column index + * @returns {MatrixIndex} + */ + Matrix.prototype.minColumnIndex = function minColumnIndex(index) { + this.checkColumnIndex(index); + var v = Infinity; + var idx = { + row: 0, + column: index + }; + for (var i = 0, ii = this.rows; i < ii; i++) { + if (this[i][index] < v) { + v = this[i][index]; + idx.row = i; + } + } + return idx; + }; - var thisTP = nTP - currentTP - this.tp[j]; - var thisFP = nFP - currentFP - this.fp[j]; + /** + * Returns an array containing the diagonal values of the matrix + * @returns {Array} + */ + Matrix.prototype.diag = function diag() { + if (!this.isSquare()) + throw new TypeError('Only square matrices have a diagonal.'); + var diag = new Array(this.rows); + for (var i = 0, ii = this.rows; i < ii; i++) { + diag[i] = this[i][i]; + } + return diag; + }; - currentTP += thisTP; - currentFP += thisFP; + /** + * Returns the sum of all elements of the matrix + * @returns {number} + */ + Matrix.prototype.sum = function sum() { + var v = 0; + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + v += this[i][j]; + } + } + return v; + }; - interValues.push(thisFP); - intraValues.push(thisTP); + /** + * Returns the mean of all elements of the matrix + * @returns {number} + */ + Matrix.prototype.mean = function mean() { + return this.sum() / this.size; + }; - interCumPercent.push(100 - (nFP - this.fp[j]) / nFP * 100); - intraCumPercent.push(100 - (nTP - this.tp[j]) / nTP * 100); + /** + * Returns the product of all elements of the matrix + * @returns {number} + */ + Matrix.prototype.prod = function prod() { + var prod = 1; + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + prod *= this[i][j]; } + } + return prod; + }; - return { - xLabels: xLabels, - interValues: interValues, - intraValues: intraValues, - interCumPercent: interCumPercent, - intraCumPercent: intraCumPercent - }; + /** + * Computes the cumulative sum of the matrix elements (in place, row by row) + * @returns {Matrix} this + */ + Matrix.prototype.cumulativeSum = function cumulativeSum() { + var sum = 0; + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + sum += this[i][j]; + this[i][j] = sum; + } } - } + return this; + }; - Performance.names = { - acc: 'Accuracy', - err: 'Error rate', - fpr: 'False positive rate', - tpr: 'True positive rate', - fnr: 'False negative rate', - tnr: 'True negative rate', - ppv: 'Positive predictive value', - npv: 'Negative predictive value', - pcfall: 'Prediction-conditioned fallout', - pcmiss: 'Prediction-conditioned miss', - lift: 'Lift value', - rpp: 'Rate of positive predictions', - rnp: 'Rate of negative predictions', - threshold: 'Threshold' + /** + * Computes the dot (scalar) product between the matrix and another + * @param {Matrix} other vector + * @returns {number} + */ + Matrix.prototype.dot = function dot(other) { + if (this.size !== other.size) + throw new RangeError('vectors do not have the same size'); + var vector1 = this.to1DArray(); + var vector2 = other.to1DArray(); + var dot = 0, l = vector1.length; + for (var i = 0; i < l; i++) { + dot += vector1[i] * vector2[i]; + } + return dot; }; - module.exports = Performance; + /** + * Returns the matrix product between this and other + * @returns {Matrix} + */ + Matrix.prototype.mmul = function mmul(other) { + if (!Matrix.isMatrix(other)) + throw new TypeError('parameter "other" must be a matrix'); + if (this.columns !== other.rows) + console.warn('Number of columns of left matrix are not equal to number of rows of right matrix.'); + var m = this.rows, n = this.columns, p = other.columns; + var result = new Matrix(m, p); -/***/ }, -/* 106 */ -/***/ function(module, exports) { + var Bcolj = new Array(n); + var i, j, k; + for (j = 0; j < p; j++) { + for (k = 0; k < n; k++) + Bcolj[k] = other[k][j]; - 'use strict'; + for (i = 0; i < m; i++) { + var Arowi = this[i]; - // Accuracy - exports.acc = pred => { - const l = pred.cutoffs.length; - const result = new Array(l); - for (var i = 0; i < l; i++) { - result[i] = (pred.tn[i] + pred.tp[i]) / (l - 1); + var s = 0; + for (k = 0; k < n; k++) + s += Arowi[k] * Bcolj[k]; + + result[i][j] = s; + } } return result; }; - // Error rate - exports.err = pred => { - const l = pred.cutoffs.length; - const result = new Array(l); - for (var i = 0; i < l; i++) { - result[i] = (pred.fn[i] + pred.fp[i] / (l - 1)); + /** + * Sorts the rows (in place) + * @param {function} compareFunction - usual Array.prototype.sort comparison function + * @returns {Matrix} this + */ + Matrix.prototype.sortRows = function sortRows(compareFunction) { + for (var i = 0, ii = this.rows; i < ii; i++) { + this[i].sort(compareFunction); } - return result; + return this; }; - // False positive rate - exports.fpr = pred => { - const l = pred.cutoffs.length; - const result = new Array(l); - for (var i = 0; i < l; i++) { - result[i] = pred.fp[i] / pred.nNeg; + /** + * Sorts the columns (in place) + * @param {function} compareFunction - usual Array.prototype.sort comparison function + * @returns {Matrix} this + */ + Matrix.prototype.sortColumns = function sortColumns(compareFunction) { + for (var i = 0, ii = this.columns; i < ii; i++) { + this.setColumn(i, this.getColumn(i).sort(compareFunction)); } - return result; + return this; }; - // True positive rate - exports.tpr = pred => { - const l = pred.cutoffs.length; - const result = new Array(l); - for (var i = 0; i < l; i++) { - result[i] = pred.tp[i] / pred.nPos; + /** + * Transposes the matrix and returns a new one containing the result + * @returns {Matrix} + */ + Matrix.prototype.transpose = function transpose() { + var result = new Matrix(this.columns, this.rows); + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + result[j][i] = this[i][j]; + } } return result; }; - // False negative rate - exports.fnr = pred => { - const l = pred.cutoffs.length; - const result = new Array(l); - for (var i = 0; i < l; i++) { - result[i] = pred.fn[i] / pred.nPos; + /** + * Returns a subset of the matrix + * @param {number} startRow - First row index + * @param {number} endRow - Last row index + * @param {number} startColumn - First column index + * @param {number} endColumn - Last column index + * @returns {Matrix} + */ + Matrix.prototype.subMatrix = function subMatrix(startRow, endRow, startColumn, endColumn) { + if ((startRow > endRow) || (startColumn > endColumn) || (startRow < 0) || (startRow >= this.rows) || (endRow < 0) || (endRow >= this.rows) || (startColumn < 0) || (startColumn >= this.columns) || (endColumn < 0) || (endColumn >= this.columns)) + throw new RangeError('Argument out of range'); + var newMatrix = new Matrix(endRow - startRow + 1, endColumn - startColumn + 1); + for (var i = startRow; i <= endRow; i++) { + for (var j = startColumn; j <= endColumn; j++) { + newMatrix[i - startRow][j - startColumn] = this[i][j]; + } } - return result; + return newMatrix; }; - // True negative rate - exports.tnr = pred => { - const l = pred.cutoffs.length; - const result = new Array(l); + /** + * Returns a subset of the matrix based on an array of row indices + * @param {Array} indices - Array containing the row indices + * @param {number} [startColumn = 0] - First column index + * @param {number} [endColumn = this.columns-1] - Last column index + * @returns {Matrix} + */ + Matrix.prototype.subMatrixRow = function subMatrixRow(indices, startColumn, endColumn) { + if (typeof startColumn === 'undefined') { + startColumn = 0; + endColumn = this.columns - 1; + } else if (typeof endColumn === 'undefined') { + endColumn = this.columns - 1; + } + if ((startColumn > endColumn) || (startColumn < 0) || (startColumn >= this.columns) || (endColumn < 0) || (endColumn >= this.columns)) + throw new RangeError('Argument out of range.'); + var l = indices.length, rows = this.rows, + X = new Matrix(l, endColumn - startColumn + 1); for (var i = 0; i < l; i++) { - result[i] = pred.tn[i] / pred.nNeg; + for (var j = startColumn; j <= endColumn; j++) { + if ((indices[i] < 0) || (indices[i] >= rows)) + throw new RangeError('Argument out of range.'); + X[i][j - startColumn] = this[indices[i]][j]; + } } - return result; + return X; }; - // Positive predictive value - exports.ppv = pred => { - const l = pred.cutoffs.length; - const result = new Array(l); + /** + * Returns a subset of the matrix based on an array of column indices + * @param {Array} indices - Array containing the column indices + * @param {number} [startRow = 0] - First row index + * @param {number} [endRow = this.rows-1] - Last row index + * @returns {Matrix} + */ + Matrix.prototype.subMatrixColumn = function subMatrixColumn(indices, startRow, endRow) { + if (typeof startRow === 'undefined') { + startRow = 0; + endRow = this.rows - 1; + } else if (typeof endRow === 'undefined') { + endRow = this.rows - 1; + } + if ((startRow > endRow) || (startRow < 0) || (startRow >= this.rows) || (endRow < 0) || (endRow >= this.rows)) + throw new RangeError('Argument out of range.'); + var l = indices.length, columns = this.columns, + X = new Matrix(endRow - startRow + 1, l); for (var i = 0; i < l; i++) { - result[i] = (pred.fp[i] + pred.tp[i] !== 0) ? (pred.tp[i] / (pred.fp[i] + pred.tp[i])) : 0; + for (var j = startRow; j <= endRow; j++) { + if ((indices[i] < 0) || (indices[i] >= columns)) + throw new RangeError('Argument out of range.'); + X[j - startRow][i] = this[j][indices[i]]; + } } - return result; + return X; }; - // Negative predictive value - exports.npv = pred => { - const l = pred.cutoffs.length; - const result = new Array(l); - for (var i = 0; i < l; i++) { - result[i] = (pred.fn[i] + pred.tn[i] !== 0) ? (pred.tn[i] / (pred.fn[i] + pred.tn[i])) : 0; + /** + * Returns the trace of the matrix (sum of the diagonal elements) + * @returns {number} + */ + Matrix.prototype.trace = function trace() { + if (!this.isSquare()) + throw new TypeError('The matrix is not square'); + var trace = 0, i = 0, l = this.rows; + for (; i < l; i++) { + trace += this[i][i]; } - return result; + return trace; }; - // Prediction conditioned fallout - exports.pcfall = pred => { - const l = pred.cutoffs.length; - const result = new Array(l); - for (var i = 0; i < l; i++) { - result[i] = (pred.fp[i] + pred.tp[i] !== 0) ? 1 - (pred.tp[i] / (pred.fp[i] + pred.tp[i])) : 1; + /** + * Sets each element of the matrix to its absolute value + * @returns {Matrix} this + */ + Matrix.prototype.abs = function abs() { + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + this[i][j] = Math.abs(this[i][j]); + } } - return result; }; - // Prediction conditioned miss - exports.pcmiss = pred => { - const l = pred.cutoffs.length; - const result = new Array(l); - for (var i = 0; i < l; i++) { - result[i] = (pred.fn[i] + pred.tn[i] !== 0) ? 1 - (pred.tn[i] / (pred.fn[i] + pred.tn[i])) : 1; - } - return result; - }; + module.exports = Matrix; - // Lift value - exports.lift = pred => { - const l = pred.cutoffs.length; - const result = new Array(l); - for (var i = 0; i < l; i++) { - result[i] = (pred.nPosPred[i] !== 0) ? ((pred.tp[i] / pred.nPos) / (pred.nPosPred[i] / pred.nSamples)) : 0; - } - return result; - }; - // Rate of positive predictions - exports.rpp = pred => { - const l = pred.cutoffs.length; - const result = new Array(l); - for (var i = 0; i < l; i++) { - result[i] = pred.nPosPred[i] / pred.nSamples; - } - return result; +/***/ }, +/* 105 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var Matrix = __webpack_require__(104); + + var SingularValueDecomposition = __webpack_require__(106); + var EigenvalueDecomposition = __webpack_require__(108); + var LuDecomposition = __webpack_require__(109); + var QrDecomposition = __webpack_require__(110); + var CholeskyDecomposition = __webpack_require__(111); + + function inverse(matrix) { + return solve(matrix, Matrix.eye(matrix.rows)); + } + + Matrix.prototype.inverse = function () { + return inverse(this); }; - // Rate of negative predictions - exports.rnp = pred => { - const l = pred.cutoffs.length; - const result = new Array(l); - for (var i = 0; i < l; i++) { - result[i] = pred.nNegPred[i] / pred.nSamples; - } - return result; + function solve(leftHandSide, rightHandSide) { + return leftHandSide.isSquare() ? new LuDecomposition(leftHandSide).solve(rightHandSide) : new QrDecomposition(leftHandSide).solve(rightHandSide); + } + + Matrix.prototype.solve = function (other) { + return solve(this, other); }; - // Threshold - exports.threshold = pred => { - const clone = pred.cutoffs.slice(); - clone[0] = clone[1]; // Remove the infinite value - return clone; + module.exports = { + SingularValueDecomposition: SingularValueDecomposition, + SVD: SingularValueDecomposition, + EigenvalueDecomposition: EigenvalueDecomposition, + EVD: EigenvalueDecomposition, + LuDecomposition: LuDecomposition, + LU: LuDecomposition, + QrDecomposition: QrDecomposition, + QR: QrDecomposition, + CholeskyDecomposition: CholeskyDecomposition, + CHO: CholeskyDecomposition, + inverse: inverse, + solve: solve }; /***/ }, -/* 107 */ -/***/ function(module, exports) { +/* 106 */ +/***/ function(module, exports, __webpack_require__) { - "use strict"; + 'use strict'; - Object.defineProperty(exports, "__esModule", { - value: true - }); + var Matrix = __webpack_require__(104); + var hypotenuse = __webpack_require__(107).hypotenuse; - var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + // https://github.com/lutzroeder/Mapack/blob/master/Source/SingularValueDecomposition.cs + function SingularValueDecomposition(value, options) { + if (!(this instanceof SingularValueDecomposition)) { + return new SingularValueDecomposition(value, options); + } + value = Matrix.checkMatrix(value); - function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + options = options || {}; - var LOOP = 8; - var FLOAT_MUL = 1 / 16777216; + var a = value.clone(), + m = value.rows, + n = value.columns, + nu = Math.min(m, n); - function multiply_uint32(n, m) { - n >>>= 0; - m >>>= 0; - var nlo = n & 0xffff; - var nhi = n - nlo; - return (nhi * m >>> 0) + nlo * m >>> 0; - } + var wantu = true, wantv = true; + if (options.computeLeftSingularVectors === false) + wantu = false; + if (options.computeRightSingularVectors === false) + wantv = false; + var autoTranspose = options.autoTranspose === true; - var XSadd = (function () { - function XSadd() { - var seed = arguments.length <= 0 || arguments[0] === undefined ? Date.now() : arguments[0]; + var swapped = false; + if (m < n) { + if (!autoTranspose) { + console.warn('Computing SVD on a matrix with more columns than rows. Consider enabling autoTranspose'); + } else { + a = a.transpose(); + m = a.rows; + n = a.columns; + swapped = true; + var aux = wantu; + wantu = wantv; + wantv = aux; + } + } - _classCallCheck(this, XSadd); + var s = new Array(Math.min(m + 1, n)), + U = Matrix.zeros(m, nu), + V = Matrix.zeros(n, n), + e = new Array(n), + work = new Array(m); - this.state = new Uint32Array(4); - this.init(seed); - } + var nct = Math.min(m - 1, n); + var nrt = Math.max(0, Math.min(n - 2, m)); - _createClass(XSadd, [{ - key: "init", - value: function init(seed) { - this.state[0] = seed; - this.state[1] = 0; - this.state[2] = 0; - this.state[3] = 0; - for (var i = 1; i < LOOP; i++) { - this.state[i & 3] ^= i + multiply_uint32(1812433253, this.state[i - 1 & 3] ^ this.state[i - 1 & 3] >>> 30 >>> 0) >>> 0; + var i, j, k, p, t, ks, f, cs, sn, max, kase, + scale, sp, spm1, epm1, sk, ek, b, c, shift, g; + + for (k = 0, max = Math.max(nct, nrt); k < max; k++) { + if (k < nct) { + s[k] = 0; + for (i = k; i < m; i++) { + s[k] = hypotenuse(s[k], a[i][k]); } - period_certification(this); - for (var i = 0; i < LOOP; i++) { - next_state(this); + if (s[k] !== 0) { + if (a[k][k] < 0) { + s[k] = -s[k]; + } + for (i = k; i < m; i++) { + a[i][k] /= s[k]; + } + a[k][k] += 1; } + s[k] = -s[k]; } - /** - * Returns a 32-bit integer r (0 <= r < 2^32) - */ - }, { - key: "getUint32", - value: function getUint32() { - next_state(this); - return this.state[3] + this.state[2] >>> 0; + for (j = k + 1; j < n; j++) { + if ((k < nct) && (s[k] !== 0)) { + t = 0; + for (i = k; i < m; i++) { + t += a[i][k] * a[i][j]; + } + t = -t / a[k][k]; + for (i = k; i < m; i++) { + a[i][j] += t * a[i][k]; + } + } + e[j] = a[k][j]; } - /** - * Returns a floating point number r (0.0 <= r < 1.0) - */ - }, { - key: "getFloat", - value: function getFloat() { - return (this.getUint32() >>> 8) * FLOAT_MUL; + if (wantu && (k < nct)) { + for (i = k; i < m; i++) { + U[i][k] = a[i][k]; + } } - }, { - key: "random", - get: function get() { - if (!this._random) { - this._random = this.getFloat.bind(this); + + if (k < nrt) { + e[k] = 0; + for (i = k + 1; i < n; i++) { + e[k] = hypotenuse(e[k], e[i]); + } + if (e[k] !== 0) { + if (e[k + 1] < 0) + e[k] = -e[k]; + for (i = k + 1; i < n; i++) { + e[i] /= e[k]; + } + e[k + 1] += 1; + } + e[k] = -e[k]; + if ((k + 1 < m) && (e[k] !== 0)) { + for (i = k + 1; i < m; i++) { + work[i] = 0; + } + for (j = k + 1; j < n; j++) { + for (i = k + 1; i < m; i++) { + work[i] += e[j] * a[i][j]; + } + } + for (j = k + 1; j < n; j++) { + t = -e[j] / e[k + 1]; + for (i = k + 1; i < m; i++) { + a[i][j] += t * work[i]; + } + } + } + if (wantv) { + for (i = k + 1; i < n; i++) { + V[i][k] = e[i]; + } } - return this._random; } - }]); + } - return XSadd; - })(); + p = Math.min(n, m + 1); + if (nct < n) { + s[nct] = a[nct][nct]; + } + if (m < p) { + s[p - 1] = 0; + } + if (nrt + 1 < p) { + e[nrt] = a[nrt][p - 1]; + } + e[p - 1] = 0; + + if (wantu) { + for (j = nct; j < nu; j++) { + for (i = 0; i < m; i++) { + U[i][j] = 0; + } + U[j][j] = 1; + } + for (k = nct - 1; k >= 0; k--) { + if (s[k] !== 0) { + for (j = k + 1; j < nu; j++) { + t = 0; + for (i = k; i < m; i++) { + t += U[i][k] * U[i][j]; + } + t = -t / U[k][k]; + for (i = k; i < m; i++) { + U[i][j] += t * U[i][k]; + } + } + for (i = k; i < m; i++) { + U[i][k] = -U[i][k]; + } + U[k][k] = 1 + U[k][k]; + for (i = 0; i < k - 1; i++) { + U[i][k] = 0; + } + } else { + for (i = 0; i < m; i++) { + U[i][k] = 0; + } + U[k][k] = 1; + } + } + } + + if (wantv) { + for (k = n - 1; k >= 0; k--) { + if ((k < nrt) && (e[k] !== 0)) { + for (j = k + 1; j < n; j++) { + t = 0; + for (i = k + 1; i < n; i++) { + t += V[i][k] * V[i][j]; + } + t = -t / V[k + 1][k]; + for (i = k + 1; i < n; i++) { + V[i][j] += t * V[i][k]; + } + } + } + for (i = 0; i < n; i++) { + V[i][k] = 0; + } + V[k][k] = 1; + } + } + + var pp = p - 1, + iter = 0, + eps = Math.pow(2, -52); + while (p > 0) { + for (k = p - 2; k >= -1; k--) { + if (k === -1) { + break; + } + if (Math.abs(e[k]) <= eps * (Math.abs(s[k]) + Math.abs(s[k + 1]))) { + e[k] = 0; + break; + } + } + if (k === p - 2) { + kase = 4; + } else { + for (ks = p - 1; ks >= k; ks--) { + if (ks === k) { + break; + } + t = (ks !== p ? Math.abs(e[ks]) : 0) + (ks !== k + 1 ? Math.abs(e[ks - 1]) : 0); + if (Math.abs(s[ks]) <= eps * t) { + s[ks] = 0; + break; + } + } + if (ks === k) { + kase = 3; + } else if (ks === p - 1) { + kase = 1; + } else { + kase = 2; + k = ks; + } + } + + k++; + + switch (kase) { + case 1: { + f = e[p - 2]; + e[p - 2] = 0; + for (j = p - 2; j >= k; j--) { + t = hypotenuse(s[j], f); + cs = s[j] / t; + sn = f / t; + s[j] = t; + if (j !== k) { + f = -sn * e[j - 1]; + e[j - 1] = cs * e[j - 1]; + } + if (wantv) { + for (i = 0; i < n; i++) { + t = cs * V[i][j] + sn * V[i][p - 1]; + V[i][p - 1] = -sn * V[i][j] + cs * V[i][p - 1]; + V[i][j] = t; + } + } + } + break; + } + case 2 : { + f = e[k - 1]; + e[k - 1] = 0; + for (j = k; j < p; j++) { + t = hypotenuse(s[j], f); + cs = s[j] / t; + sn = f / t; + s[j] = t; + f = -sn * e[j]; + e[j] = cs * e[j]; + if (wantu) { + for (i = 0; i < m; i++) { + t = cs * U[i][j] + sn * U[i][k - 1]; + U[i][k - 1] = -sn * U[i][j] + cs * U[i][k - 1]; + U[i][j] = t; + } + } + } + break; + } + case 3 : { + scale = Math.max(Math.max(Math.max(Math.max(Math.abs(s[p - 1]), Math.abs(s[p - 2])), Math.abs(e[p - 2])), Math.abs(s[k])), Math.abs(e[k])); + sp = s[p - 1] / scale; + spm1 = s[p - 2] / scale; + epm1 = e[p - 2] / scale; + sk = s[k] / scale; + ek = e[k] / scale; + b = ((spm1 + sp) * (spm1 - sp) + epm1 * epm1) / 2; + c = (sp * epm1) * (sp * epm1); + shift = 0; + if ((b !== 0) || (c !== 0)) { + shift = Math.sqrt(b * b + c); + if (b < 0) { + shift = -shift; + } + shift = c / (b + shift); + } + f = (sk + sp) * (sk - sp) + shift; + g = sk * ek; + for (j = k; j < p - 1; j++) { + t = hypotenuse(f, g); + cs = f / t; + sn = g / t; + if (j !== k) { + e[j - 1] = t; + } + f = cs * s[j] + sn * e[j]; + e[j] = cs * e[j] - sn * s[j]; + g = sn * s[j + 1]; + s[j + 1] = cs * s[j + 1]; + if (wantv) { + for (i = 0; i < n; i++) { + t = cs * V[i][j] + sn * V[i][j + 1]; + V[i][j + 1] = -sn * V[i][j] + cs * V[i][j + 1]; + V[i][j] = t; + } + } + t = hypotenuse(f, g); + cs = f / t; + sn = g / t; + s[j] = t; + f = cs * e[j] + sn * s[j + 1]; + s[j + 1] = -sn * e[j] + cs * s[j + 1]; + g = sn * e[j + 1]; + e[j + 1] = cs * e[j + 1]; + if (wantu && (j < m - 1)) { + for (i = 0; i < m; i++) { + t = cs * U[i][j] + sn * U[i][j + 1]; + U[i][j + 1] = -sn * U[i][j] + cs * U[i][j + 1]; + U[i][j] = t; + } + } + } + e[p - 2] = f; + iter = iter + 1; + break; + } + case 4: { + if (s[k] <= 0) { + s[k] = (s[k] < 0 ? -s[k] : 0); + if (wantv) { + for (i = 0; i <= pp; i++) { + V[i][k] = -V[i][k]; + } + } + } + while (k < pp) { + if (s[k] >= s[k + 1]) { + break; + } + t = s[k]; + s[k] = s[k + 1]; + s[k + 1] = t; + if (wantv && (k < n - 1)) { + for (i = 0; i < n; i++) { + t = V[i][k + 1]; + V[i][k + 1] = V[i][k]; + V[i][k] = t; + } + } + if (wantu && (k < m - 1)) { + for (i = 0; i < m; i++) { + t = U[i][k + 1]; + U[i][k + 1] = U[i][k]; + U[i][k] = t; + } + } + k++; + } + iter = 0; + p--; + break; + } + } + } + + if (swapped) { + var tmp = V; + V = U; + U = tmp; + } + + this.m = m; + this.n = n; + this.s = s; + this.U = U; + this.V = V; + } + + SingularValueDecomposition.prototype = { + get condition() { + return this.s[0] / this.s[Math.min(this.m, this.n) - 1]; + }, + get norm2() { + return this.s[0]; + }, + get rank() { + var eps = Math.pow(2, -52), + tol = Math.max(this.m, this.n) * this.s[0] * eps, + r = 0, + s = this.s; + for (var i = 0, ii = s.length; i < ii; i++) { + if (s[i] > tol) { + r++; + } + } + return r; + }, + get diagonal() { + return this.s; + }, + // https://github.com/accord-net/framework/blob/development/Sources/Accord.Math/Decompositions/SingularValueDecomposition.cs + get threshold() { + return (Math.pow(2, -52) / 2) * Math.max(this.m, this.n) * this.s[0]; + }, + get leftSingularVectors() { + return this.U; + }, + get rightSingularVectors() { + return this.V; + }, + get diagonalMatrix() { + return Matrix.diag(this.s); + }, + solve: function (value) { + + var Y = value, + e = this.threshold, + scols = this.s.length, + Ls = Matrix.zeros(scols, scols), + i; + + for (i = 0; i < scols; i++) { + if (Math.abs(this.s[i]) <= e) { + Ls[i][i] = 0; + } else { + Ls[i][i] = 1 / this.s[i]; + } + } + + + var VL = this.V.mmul(Ls), + vrows = this.V.rows, + urows = this.U.rows, + VLU = Matrix.zeros(vrows, urows), + j, k, sum; + + for (i = 0; i < vrows; i++) { + for (j = 0; j < urows; j++) { + sum = 0; + for (k = 0; k < scols; k++) { + sum += VL[i][k] * this.U[j][k]; + } + VLU[i][j] = sum; + } + } + + return VLU.mmul(Y); + }, + solveForDiagonal: function (value) { + return this.solve(Matrix.diag(value)); + }, + inverse: function () { + var e = this.threshold, + vrows = this.V.rows, + vcols = this.V.columns, + X = new Matrix(vrows, this.s.length), + i, j; + + for (i = 0; i < vrows; i++) { + for (j = 0; j < vcols; j++) { + if (Math.abs(this.s[j]) > e) { + X[i][j] = this.V[i][j] / this.s[j]; + } else { + X[i][j] = 0; + } + } + } + + var urows = this.U.rows, + ucols = this.U.columns, + Y = new Matrix(vrows, urows), + k, sum; + + for (i = 0; i < vrows; i++) { + for (j = 0; j < urows; j++) { + sum = 0; + for (k = 0; k < ucols; k++) { + sum += X[i][k] * this.U[j][k]; + } + Y[i][j] = sum; + } + } + + return Y; + } + }; + + module.exports = SingularValueDecomposition; + + +/***/ }, +/* 107 */ +/***/ function(module, exports) { + + 'use strict'; + + exports.hypotenuse = function hypotenuse(a, b) { + var r; + if (Math.abs(a) > Math.abs(b)) { + r = b / a; + return Math.abs(a) * Math.sqrt(1 + r * r); + } + if (b !== 0) { + r = a / b; + return Math.abs(b) * Math.sqrt(1 + r * r); + } + return 0; + }; + + +/***/ }, +/* 108 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var Matrix = __webpack_require__(104); + var hypotenuse = __webpack_require__(107).hypotenuse; + + // https://github.com/lutzroeder/Mapack/blob/master/Source/EigenvalueDecomposition.cs + function EigenvalueDecomposition(matrix) { + if (!(this instanceof EigenvalueDecomposition)) { + return new EigenvalueDecomposition(matrix); + } + matrix = Matrix.checkMatrix(matrix); + if (!matrix.isSquare()) { + throw new Error('Matrix is not a square matrix'); + } + + var n = matrix.columns, + V = Matrix.zeros(n, n), + d = new Array(n), + e = new Array(n), + value = matrix, + i, j; + + if (matrix.isSymmetric()) { + for (i = 0; i < n; i++) { + for (j = 0; j < n; j++) { + V[i][j] = value[i][j]; + } + } + tred2(n, e, d, V); + tql2(n, e, d, V); + } + else { + var H = Matrix.zeros(n, n), + ort = new Array(n); + for (j = 0; j < n; j++) { + for (i = 0; i < n; i++) { + H[i][j] = value[i][j]; + } + } + orthes(n, H, ort, V); + hqr2(n, e, d, V, H); + } + + this.n = n; + this.e = e; + this.d = d; + this.V = V; + } + + EigenvalueDecomposition.prototype = { + get realEigenvalues() { + return this.d; + }, + get imaginaryEigenvalues() { + return this.e; + }, + get eigenvectorMatrix() { + return this.V; + }, + get diagonalMatrix() { + var n = this.n, + e = this.e, + d = this.d, + X = new Matrix(n, n), + i, j; + for (i = 0; i < n; i++) { + for (j = 0; j < n; j++) { + X[i][j] = 0; + } + X[i][i] = d[i]; + if (e[i] > 0) { + X[i][i + 1] = e[i]; + } + else if (e[i] < 0) { + X[i][i - 1] = e[i]; + } + } + return X; + } + }; + + function tred2(n, e, d, V) { + + var f, g, h, i, j, k, + hh, scale; + + for (j = 0; j < n; j++) { + d[j] = V[n - 1][j]; + } + + for (i = n - 1; i > 0; i--) { + scale = 0; + h = 0; + for (k = 0; k < i; k++) { + scale = scale + Math.abs(d[k]); + } + + if (scale === 0) { + e[i] = d[i - 1]; + for (j = 0; j < i; j++) { + d[j] = V[i - 1][j]; + V[i][j] = 0; + V[j][i] = 0; + } + } else { + for (k = 0; k < i; k++) { + d[k] /= scale; + h += d[k] * d[k]; + } + + f = d[i - 1]; + g = Math.sqrt(h); + if (f > 0) { + g = -g; + } + + e[i] = scale * g; + h = h - f * g; + d[i - 1] = f - g; + for (j = 0; j < i; j++) { + e[j] = 0; + } + + for (j = 0; j < i; j++) { + f = d[j]; + V[j][i] = f; + g = e[j] + V[j][j] * f; + for (k = j + 1; k <= i - 1; k++) { + g += V[k][j] * d[k]; + e[k] += V[k][j] * f; + } + e[j] = g; + } + + f = 0; + for (j = 0; j < i; j++) { + e[j] /= h; + f += e[j] * d[j]; + } + + hh = f / (h + h); + for (j = 0; j < i; j++) { + e[j] -= hh * d[j]; + } + + for (j = 0; j < i; j++) { + f = d[j]; + g = e[j]; + for (k = j; k <= i - 1; k++) { + V[k][j] -= (f * e[k] + g * d[k]); + } + d[j] = V[i - 1][j]; + V[i][j] = 0; + } + } + d[i] = h; + } + + for (i = 0; i < n - 1; i++) { + V[n - 1][i] = V[i][i]; + V[i][i] = 1; + h = d[i + 1]; + if (h !== 0) { + for (k = 0; k <= i; k++) { + d[k] = V[k][i + 1] / h; + } + + for (j = 0; j <= i; j++) { + g = 0; + for (k = 0; k <= i; k++) { + g += V[k][i + 1] * V[k][j]; + } + for (k = 0; k <= i; k++) { + V[k][j] -= g * d[k]; + } + } + } + + for (k = 0; k <= i; k++) { + V[k][i + 1] = 0; + } + } + + for (j = 0; j < n; j++) { + d[j] = V[n - 1][j]; + V[n - 1][j] = 0; + } + + V[n - 1][n - 1] = 1; + e[0] = 0; + } + + function tql2(n, e, d, V) { + + var g, h, i, j, k, l, m, p, r, + dl1, c, c2, c3, el1, s, s2, + iter; + + for (i = 1; i < n; i++) { + e[i - 1] = e[i]; + } + + e[n - 1] = 0; + + var f = 0, + tst1 = 0, + eps = Math.pow(2, -52); + + for (l = 0; l < n; l++) { + tst1 = Math.max(tst1, Math.abs(d[l]) + Math.abs(e[l])); + m = l; + while (m < n) { + if (Math.abs(e[m]) <= eps * tst1) { + break; + } + m++; + } + + if (m > l) { + iter = 0; + do { + iter = iter + 1; + + g = d[l]; + p = (d[l + 1] - g) / (2 * e[l]); + r = hypotenuse(p, 1); + if (p < 0) { + r = -r; + } + + d[l] = e[l] / (p + r); + d[l + 1] = e[l] * (p + r); + dl1 = d[l + 1]; + h = g - d[l]; + for (i = l + 2; i < n; i++) { + d[i] -= h; + } + + f = f + h; + + p = d[m]; + c = 1; + c2 = c; + c3 = c; + el1 = e[l + 1]; + s = 0; + s2 = 0; + for (i = m - 1; i >= l; i--) { + c3 = c2; + c2 = c; + s2 = s; + g = c * e[i]; + h = c * p; + r = hypotenuse(p, e[i]); + e[i + 1] = s * r; + s = e[i] / r; + c = p / r; + p = c * d[i] - s * g; + d[i + 1] = h + s * (c * g + s * d[i]); + + for (k = 0; k < n; k++) { + h = V[k][i + 1]; + V[k][i + 1] = s * V[k][i] + c * h; + V[k][i] = c * V[k][i] - s * h; + } + } + + p = -s * s2 * c3 * el1 * e[l] / dl1; + e[l] = s * p; + d[l] = c * p; + + } + while (Math.abs(e[l]) > eps * tst1); + } + d[l] = d[l] + f; + e[l] = 0; + } + + for (i = 0; i < n - 1; i++) { + k = i; + p = d[i]; + for (j = i + 1; j < n; j++) { + if (d[j] < p) { + k = j; + p = d[j]; + } + } + + if (k !== i) { + d[k] = d[i]; + d[i] = p; + for (j = 0; j < n; j++) { + p = V[j][i]; + V[j][i] = V[j][k]; + V[j][k] = p; + } + } + } + } + + function orthes(n, H, ort, V) { + + var low = 0, + high = n - 1, + f, g, h, i, j, m, + scale; + + for (m = low + 1; m <= high - 1; m++) { + scale = 0; + for (i = m; i <= high; i++) { + scale = scale + Math.abs(H[i][m - 1]); + } + + if (scale !== 0) { + h = 0; + for (i = high; i >= m; i--) { + ort[i] = H[i][m - 1] / scale; + h += ort[i] * ort[i]; + } + + g = Math.sqrt(h); + if (ort[m] > 0) { + g = -g; + } + + h = h - ort[m] * g; + ort[m] = ort[m] - g; + + for (j = m; j < n; j++) { + f = 0; + for (i = high; i >= m; i--) { + f += ort[i] * H[i][j]; + } + + f = f / h; + for (i = m; i <= high; i++) { + H[i][j] -= f * ort[i]; + } + } + + for (i = 0; i <= high; i++) { + f = 0; + for (j = high; j >= m; j--) { + f += ort[j] * H[i][j]; + } + + f = f / h; + for (j = m; j <= high; j++) { + H[i][j] -= f * ort[j]; + } + } + + ort[m] = scale * ort[m]; + H[m][m - 1] = scale * g; + } + } + + for (i = 0; i < n; i++) { + for (j = 0; j < n; j++) { + V[i][j] = (i === j ? 1 : 0); + } + } + + for (m = high - 1; m >= low + 1; m--) { + if (H[m][m - 1] !== 0) { + for (i = m + 1; i <= high; i++) { + ort[i] = H[i][m - 1]; + } + + for (j = m; j <= high; j++) { + g = 0; + for (i = m; i <= high; i++) { + g += ort[i] * V[i][j]; + } + + g = (g / ort[m]) / H[m][m - 1]; + for (i = m; i <= high; i++) { + V[i][j] += g * ort[i]; + } + } + } + } + } + + function hqr2(nn, e, d, V, H) { + var n = nn - 1, + low = 0, + high = nn - 1, + eps = Math.pow(2, -52), + exshift = 0, + norm = 0, + p = 0, + q = 0, + r = 0, + s = 0, + z = 0, + iter = 0, + i, j, k, l, m, t, w, x, y, + ra, sa, vr, vi, + notlast, cdivres; + + for (i = 0; i < nn; i++) { + if (i < low || i > high) { + d[i] = H[i][i]; + e[i] = 0; + } + + for (j = Math.max(i - 1, 0); j < nn; j++) { + norm = norm + Math.abs(H[i][j]); + } + } + + while (n >= low) { + l = n; + while (l > low) { + s = Math.abs(H[l - 1][l - 1]) + Math.abs(H[l][l]); + if (s === 0) { + s = norm; + } + if (Math.abs(H[l][l - 1]) < eps * s) { + break; + } + l--; + } + + if (l === n) { + H[n][n] = H[n][n] + exshift; + d[n] = H[n][n]; + e[n] = 0; + n--; + iter = 0; + } else if (l === n - 1) { + w = H[n][n - 1] * H[n - 1][n]; + p = (H[n - 1][n - 1] - H[n][n]) / 2; + q = p * p + w; + z = Math.sqrt(Math.abs(q)); + H[n][n] = H[n][n] + exshift; + H[n - 1][n - 1] = H[n - 1][n - 1] + exshift; + x = H[n][n]; + + if (q >= 0) { + z = (p >= 0) ? (p + z) : (p - z); + d[n - 1] = x + z; + d[n] = d[n - 1]; + if (z !== 0) { + d[n] = x - w / z; + } + e[n - 1] = 0; + e[n] = 0; + x = H[n][n - 1]; + s = Math.abs(x) + Math.abs(z); + p = x / s; + q = z / s; + r = Math.sqrt(p * p + q * q); + p = p / r; + q = q / r; + + for (j = n - 1; j < nn; j++) { + z = H[n - 1][j]; + H[n - 1][j] = q * z + p * H[n][j]; + H[n][j] = q * H[n][j] - p * z; + } + + for (i = 0; i <= n; i++) { + z = H[i][n - 1]; + H[i][n - 1] = q * z + p * H[i][n]; + H[i][n] = q * H[i][n] - p * z; + } + + for (i = low; i <= high; i++) { + z = V[i][n - 1]; + V[i][n - 1] = q * z + p * V[i][n]; + V[i][n] = q * V[i][n] - p * z; + } + } else { + d[n - 1] = x + p; + d[n] = x + p; + e[n - 1] = z; + e[n] = -z; + } + + n = n - 2; + iter = 0; + } else { + x = H[n][n]; + y = 0; + w = 0; + if (l < n) { + y = H[n - 1][n - 1]; + w = H[n][n - 1] * H[n - 1][n]; + } + + if (iter === 10) { + exshift += x; + for (i = low; i <= n; i++) { + H[i][i] -= x; + } + s = Math.abs(H[n][n - 1]) + Math.abs(H[n - 1][n - 2]); + x = y = 0.75 * s; + w = -0.4375 * s * s; + } + + if (iter === 30) { + s = (y - x) / 2; + s = s * s + w; + if (s > 0) { + s = Math.sqrt(s); + if (y < x) { + s = -s; + } + s = x - w / ((y - x) / 2 + s); + for (i = low; i <= n; i++) { + H[i][i] -= s; + } + exshift += s; + x = y = w = 0.964; + } + } + + iter = iter + 1; + + m = n - 2; + while (m >= l) { + z = H[m][m]; + r = x - z; + s = y - z; + p = (r * s - w) / H[m + 1][m] + H[m][m + 1]; + q = H[m + 1][m + 1] - z - r - s; + r = H[m + 2][m + 1]; + s = Math.abs(p) + Math.abs(q) + Math.abs(r); + p = p / s; + q = q / s; + r = r / s; + if (m === l) { + break; + } + if (Math.abs(H[m][m - 1]) * (Math.abs(q) + Math.abs(r)) < eps * (Math.abs(p) * (Math.abs(H[m - 1][m - 1]) + Math.abs(z) + Math.abs(H[m + 1][m + 1])))) { + break; + } + m--; + } + + for (i = m + 2; i <= n; i++) { + H[i][i - 2] = 0; + if (i > m + 2) { + H[i][i - 3] = 0; + } + } + + for (k = m; k <= n - 1; k++) { + notlast = (k !== n - 1); + if (k !== m) { + p = H[k][k - 1]; + q = H[k + 1][k - 1]; + r = (notlast ? H[k + 2][k - 1] : 0); + x = Math.abs(p) + Math.abs(q) + Math.abs(r); + if (x !== 0) { + p = p / x; + q = q / x; + r = r / x; + } + } + + if (x === 0) { + break; + } + + s = Math.sqrt(p * p + q * q + r * r); + if (p < 0) { + s = -s; + } + + if (s !== 0) { + if (k !== m) { + H[k][k - 1] = -s * x; + } else if (l !== m) { + H[k][k - 1] = -H[k][k - 1]; + } + + p = p + s; + x = p / s; + y = q / s; + z = r / s; + q = q / p; + r = r / p; + + for (j = k; j < nn; j++) { + p = H[k][j] + q * H[k + 1][j]; + if (notlast) { + p = p + r * H[k + 2][j]; + H[k + 2][j] = H[k + 2][j] - p * z; + } + + H[k][j] = H[k][j] - p * x; + H[k + 1][j] = H[k + 1][j] - p * y; + } + + for (i = 0; i <= Math.min(n, k + 3); i++) { + p = x * H[i][k] + y * H[i][k + 1]; + if (notlast) { + p = p + z * H[i][k + 2]; + H[i][k + 2] = H[i][k + 2] - p * r; + } + + H[i][k] = H[i][k] - p; + H[i][k + 1] = H[i][k + 1] - p * q; + } + + for (i = low; i <= high; i++) { + p = x * V[i][k] + y * V[i][k + 1]; + if (notlast) { + p = p + z * V[i][k + 2]; + V[i][k + 2] = V[i][k + 2] - p * r; + } + + V[i][k] = V[i][k] - p; + V[i][k + 1] = V[i][k + 1] - p * q; + } + } + } + } + } + + if (norm === 0) { + return; + } + + for (n = nn - 1; n >= 0; n--) { + p = d[n]; + q = e[n]; + + if (q === 0) { + l = n; + H[n][n] = 1; + for (i = n - 1; i >= 0; i--) { + w = H[i][i] - p; + r = 0; + for (j = l; j <= n; j++) { + r = r + H[i][j] * H[j][n]; + } + + if (e[i] < 0) { + z = w; + s = r; + } else { + l = i; + if (e[i] === 0) { + H[i][n] = (w !== 0) ? (-r / w) : (-r / (eps * norm)); + } else { + x = H[i][i + 1]; + y = H[i + 1][i]; + q = (d[i] - p) * (d[i] - p) + e[i] * e[i]; + t = (x * s - z * r) / q; + H[i][n] = t; + H[i + 1][n] = (Math.abs(x) > Math.abs(z)) ? ((-r - w * t) / x) : ((-s - y * t) / z); + } + + t = Math.abs(H[i][n]); + if ((eps * t) * t > 1) { + for (j = i; j <= n; j++) { + H[j][n] = H[j][n] / t; + } + } + } + } + } else if (q < 0) { + l = n - 1; + + if (Math.abs(H[n][n - 1]) > Math.abs(H[n - 1][n])) { + H[n - 1][n - 1] = q / H[n][n - 1]; + H[n - 1][n] = -(H[n][n] - p) / H[n][n - 1]; + } else { + cdivres = cdiv(0, -H[n - 1][n], H[n - 1][n - 1] - p, q); + H[n - 1][n - 1] = cdivres[0]; + H[n - 1][n] = cdivres[1]; + } + + H[n][n - 1] = 0; + H[n][n] = 1; + for (i = n - 2; i >= 0; i--) { + ra = 0; + sa = 0; + for (j = l; j <= n; j++) { + ra = ra + H[i][j] * H[j][n - 1]; + sa = sa + H[i][j] * H[j][n]; + } + + w = H[i][i] - p; + + if (e[i] < 0) { + z = w; + r = ra; + s = sa; + } else { + l = i; + if (e[i] === 0) { + cdivres = cdiv(-ra, -sa, w, q); + H[i][n - 1] = cdivres[0]; + H[i][n] = cdivres[1]; + } else { + x = H[i][i + 1]; + y = H[i + 1][i]; + vr = (d[i] - p) * (d[i] - p) + e[i] * e[i] - q * q; + vi = (d[i] - p) * 2 * q; + if (vr === 0 && vi === 0) { + vr = eps * norm * (Math.abs(w) + Math.abs(q) + Math.abs(x) + Math.abs(y) + Math.abs(z)); + } + cdivres = cdiv(x * r - z * ra + q * sa, x * s - z * sa - q * ra, vr, vi); + H[i][n - 1] = cdivres[0]; + H[i][n] = cdivres[1]; + if (Math.abs(x) > (Math.abs(z) + Math.abs(q))) { + H[i + 1][n - 1] = (-ra - w * H[i][n - 1] + q * H[i][n]) / x; + H[i + 1][n] = (-sa - w * H[i][n] - q * H[i][n - 1]) / x; + } else { + cdivres = cdiv(-r - y * H[i][n - 1], -s - y * H[i][n], z, q); + H[i + 1][n - 1] = cdivres[0]; + H[i + 1][n] = cdivres[1]; + } + } + + t = Math.max(Math.abs(H[i][n - 1]), Math.abs(H[i][n])); + if ((eps * t) * t > 1) { + for (j = i; j <= n; j++) { + H[j][n - 1] = H[j][n - 1] / t; + H[j][n] = H[j][n] / t; + } + } + } + } + } + } + + for (i = 0; i < nn; i++) { + if (i < low || i > high) { + for (j = i; j < nn; j++) { + V[i][j] = H[i][j]; + } + } + } + + for (j = nn - 1; j >= low; j--) { + for (i = low; i <= high; i++) { + z = 0; + for (k = low; k <= Math.min(j, high); k++) { + z = z + V[i][k] * H[k][j]; + } + V[i][j] = z; + } + } + } + + function cdiv(xr, xi, yr, yi) { + var r, d; + if (Math.abs(yr) > Math.abs(yi)) { + r = yi / yr; + d = yr + r * yi; + return [(xr + r * xi) / d, (xi - r * xr) / d]; + } + else { + r = yr / yi; + d = yi + r * yr; + return [(r * xr + xi) / d, (r * xi - xr) / d]; + } + } + + module.exports = EigenvalueDecomposition; + + +/***/ }, +/* 109 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var Matrix = __webpack_require__(104); + + // https://github.com/lutzroeder/Mapack/blob/master/Source/LuDecomposition.cs + function LuDecomposition(matrix) { + if (!(this instanceof LuDecomposition)) { + return new LuDecomposition(matrix); + } + matrix = Matrix.checkMatrix(matrix); + + var lu = matrix.clone(), + rows = lu.rows, + columns = lu.columns, + pivotVector = new Array(rows), + pivotSign = 1, + i, j, k, p, s, t, v, + LUrowi, LUcolj, kmax; + + for (i = 0; i < rows; i++) { + pivotVector[i] = i; + } + + LUcolj = new Array(rows); + + for (j = 0; j < columns; j++) { + + for (i = 0; i < rows; i++) { + LUcolj[i] = lu[i][j]; + } + + for (i = 0; i < rows; i++) { + LUrowi = lu[i]; + kmax = Math.min(i, j); + s = 0; + for (k = 0; k < kmax; k++) { + s += LUrowi[k] * LUcolj[k]; + } + LUrowi[j] = LUcolj[i] -= s; + } + + p = j; + for (i = j + 1; i < rows; i++) { + if (Math.abs(LUcolj[i]) > Math.abs(LUcolj[p])) { + p = i; + } + } + + if (p !== j) { + for (k = 0; k < columns; k++) { + t = lu[p][k]; + lu[p][k] = lu[j][k]; + lu[j][k] = t; + } + + v = pivotVector[p]; + pivotVector[p] = pivotVector[j]; + pivotVector[j] = v; + + pivotSign = -pivotSign; + } + + if (j < rows && lu[j][j] !== 0) { + for (i = j + 1; i < rows; i++) { + lu[i][j] /= lu[j][j]; + } + } + } + + this.LU = lu; + this.pivotVector = pivotVector; + this.pivotSign = pivotSign; + } + + LuDecomposition.prototype = { + isSingular: function () { + var data = this.LU, + col = data.columns; + for (var j = 0; j < col; j++) { + if (data[j][j] === 0) { + return true; + } + } + return false; + }, + get determinant() { + var data = this.LU; + if (!data.isSquare()) + throw new Error('Matrix must be square'); + var determinant = this.pivotSign, col = data.columns; + for (var j = 0; j < col; j++) + determinant *= data[j][j]; + return determinant; + }, + get lowerTriangularFactor() { + var data = this.LU, + rows = data.rows, + columns = data.columns, + X = new Matrix(rows, columns); + for (var i = 0; i < rows; i++) { + for (var j = 0; j < columns; j++) { + if (i > j) { + X[i][j] = data[i][j]; + } else if (i === j) { + X[i][j] = 1; + } else { + X[i][j] = 0; + } + } + } + return X; + }, + get upperTriangularFactor() { + var data = this.LU, + rows = data.rows, + columns = data.columns, + X = new Matrix(rows, columns); + for (var i = 0; i < rows; i++) { + for (var j = 0; j < columns; j++) { + if (i <= j) { + X[i][j] = data[i][j]; + } else { + X[i][j] = 0; + } + } + } + return X; + }, + get pivotPermutationVector() { + return this.pivotVector.slice(); + }, + solve: function (value) { + value = Matrix.checkMatrix(value); + + var lu = this.LU, + rows = lu.rows; + + if (rows !== value.rows) + throw new Error('Invalid matrix dimensions'); + if (this.isSingular()) + throw new Error('LU matrix is singular'); + + var count = value.columns, + X = value.subMatrixRow(this.pivotVector, 0, count - 1), + columns = lu.columns, + i, j, k; + + for (k = 0; k < columns; k++) { + for (i = k + 1; i < columns; i++) { + for (j = 0; j < count; j++) { + X[i][j] -= X[k][j] * lu[i][k]; + } + } + } + for (k = columns - 1; k >= 0; k--) { + for (j = 0; j < count; j++) { + X[k][j] /= lu[k][k]; + } + for (i = 0; i < k; i++) { + for (j = 0; j < count; j++) { + X[i][j] -= X[k][j] * lu[i][k]; + } + } + } + return X; + } + }; + + module.exports = LuDecomposition; + + +/***/ }, +/* 110 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var Matrix = __webpack_require__(104); + var hypotenuse = __webpack_require__(107).hypotenuse; + + //https://github.com/lutzroeder/Mapack/blob/master/Source/QrDecomposition.cs + function QrDecomposition(value) { + if (!(this instanceof QrDecomposition)) { + return new QrDecomposition(value); + } + value = Matrix.checkMatrix(value); + + var qr = value.clone(), + m = value.rows, + n = value.columns, + rdiag = new Array(n), + i, j, k, s; + + for (k = 0; k < n; k++) { + var nrm = 0; + for (i = k; i < m; i++) { + nrm = hypotenuse(nrm, qr[i][k]); + } + if (nrm !== 0) { + if (qr[k][k] < 0) { + nrm = -nrm; + } + for (i = k; i < m; i++) { + qr[i][k] /= nrm; + } + qr[k][k] += 1; + for (j = k + 1; j < n; j++) { + s = 0; + for (i = k; i < m; i++) { + s += qr[i][k] * qr[i][j]; + } + s = -s / qr[k][k]; + for (i = k; i < m; i++) { + qr[i][j] += s * qr[i][k]; + } + } + } + rdiag[k] = -nrm; + } + + this.QR = qr; + this.Rdiag = rdiag; + } + + QrDecomposition.prototype = { + solve: function (value) { + value = Matrix.checkMatrix(value); + + var qr = this.QR, + m = qr.rows; + + if (value.rows !== m) + throw new Error('Matrix row dimensions must agree'); + if (!this.isFullRank()) + throw new Error('Matrix is rank deficient'); + + var count = value.columns, + X = value.clone(), + n = qr.columns, + i, j, k, s; + + for (k = 0; k < n; k++) { + for (j = 0; j < count; j++) { + s = 0; + for (i = k; i < m; i++) { + s += qr[i][k] * X[i][j]; + } + s = -s / qr[k][k]; + for (i = k; i < m; i++) { + X[i][j] += s * qr[i][k]; + } + } + } + for (k = n - 1; k >= 0; k--) { + for (j = 0; j < count; j++) { + X[k][j] /= this.Rdiag[k]; + } + for (i = 0; i < k; i++) { + for (j = 0; j < count; j++) { + X[i][j] -= X[k][j] * qr[i][k]; + } + } + } + + return X.subMatrix(0, n - 1, 0, count - 1); + }, + isFullRank: function () { + var columns = this.QR.columns; + for (var i = 0; i < columns; i++) { + if (this.Rdiag[i] === 0) { + return false; + } + } + return true; + }, + get upperTriangularFactor() { + var qr = this.QR, + n = qr.columns, + X = new Matrix(n, n), + i, j; + for (i = 0; i < n; i++) { + for (j = 0; j < n; j++) { + if (i < j) { + X[i][j] = qr[i][j]; + } else if (i === j) { + X[i][j] = this.Rdiag[i]; + } else { + X[i][j] = 0; + } + } + } + return X; + }, + get orthogonalFactor() { + var qr = this.QR, + rows = qr.rows, + columns = qr.columns, + X = new Matrix(rows, columns), + i, j, k, s; + + for (k = columns - 1; k >= 0; k--) { + for (i = 0; i < rows; i++) { + X[i][k] = 0; + } + X[k][k] = 1; + for (j = k; j < columns; j++) { + if (qr[k][k] !== 0) { + s = 0; + for (i = k; i < rows; i++) { + s += qr[i][k] * X[i][j]; + } + + s = -s / qr[k][k]; + + for (i = k; i < rows; i++) { + X[i][j] += s * qr[i][k]; + } + } + } + } + return X; + } + }; + + module.exports = QrDecomposition; + + +/***/ }, +/* 111 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var Matrix = __webpack_require__(104); + + // https://github.com/lutzroeder/Mapack/blob/master/Source/CholeskyDecomposition.cs + function CholeskyDecomposition(value) { + if (!(this instanceof CholeskyDecomposition)) { + return new CholeskyDecomposition(value); + } + value = Matrix.checkMatrix(value); + if (!value.isSymmetric()) + throw new Error('Matrix is not symmetric'); + + var a = value, + dimension = a.rows, + l = new Matrix(dimension, dimension), + positiveDefinite = true, + i, j, k; + + for (j = 0; j < dimension; j++) { + var Lrowj = l[j]; + var d = 0; + for (k = 0; k < j; k++) { + var Lrowk = l[k]; + var s = 0; + for (i = 0; i < k; i++) { + s += Lrowk[i] * Lrowj[i]; + } + Lrowj[k] = s = (a[j][k] - s) / l[k][k]; + d = d + s * s; + } + + d = a[j][j] - d; + + positiveDefinite &= (d > 0); + l[j][j] = Math.sqrt(Math.max(d, 0)); + for (k = j + 1; k < dimension; k++) { + l[j][k] = 0; + } + } + + if (!positiveDefinite) { + throw new Error('Matrix is not positive definite'); + } + + this.L = l; + } + + CholeskyDecomposition.prototype = { + get leftTriangularFactor() { + return this.L; + }, + solve: function (value) { + value = Matrix.checkMatrix(value); + + var l = this.L, + dimension = l.rows; + + if (value.rows !== dimension) { + throw new Error('Matrix dimensions do not match'); + } + + var count = value.columns, + B = value.clone(), + i, j, k; + + for (k = 0; k < dimension; k++) { + for (j = 0; j < count; j++) { + for (i = 0; i < k; i++) { + B[k][j] -= B[i][j] * l[k][i]; + } + B[k][j] /= l[k][k]; + } + } + + for (k = dimension - 1; k >= 0; k--) { + for (j = 0; j < count; j++) { + for (i = k + 1; i < dimension; i++) { + B[k][j] -= B[i][j] * l[i][k]; + } + B[k][j] /= l[k][k]; + } + } + + return B; + } + }; + + module.exports = CholeskyDecomposition; + + +/***/ }, +/* 112 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Created by acastillo on 8/24/15. + */ + /** + * Non in-place function definitions, compatible with mathjs code * + */ + + 'use strict'; + + var Matrix = __webpack_require__(103); + + function matrix(A,B){ + return new Matrix(A,B); + } + + function ones(rows, cols){ + return Matrix.ones(rows,cols); + } + + function eye(rows, cols){ + return Matrix.eye(rows, cols); + } + + function zeros(rows, cols){ + return Matrix.zeros(rows, cols); + } + + function random(rows, cols){ + return Matrix.rand(rows,cols); + } + + function transpose(A){ + if(typeof A == 'number') + return A; + var result = A.clone(); + return result.transpose(); + } + + function add(A, B){ + if(typeof A == 'number'&&typeof B === 'number') + return A+B; + if(typeof A == 'number') + return this.add(B,A); + + var result = A.clone(); + return result.add(B); + + } + + function subtract(A, B){ + if(typeof A == 'number'&&typeof B === 'number') + return A-B; + if(typeof A == 'number') + return this.subtract(B,A); + var result = A.clone(); + return result.sub(B); + } + + function multiply(A, B){ + if(typeof A == 'number'&&typeof B === 'number') + return A*B; + if(typeof A == 'number') + return this.multiply(B,A); + + var result = A.clone(); + + if(typeof B === 'number') + result.mul(B); + else + result = result.mmul(B); + + if(result.rows==1&&result.columns==1) + return result[0][0]; + else + return result; + + } + + function dotMultiply(A, B){ + var result = A.clone(); + return result.mul(B); + } + + function dotDivide(A, B){ + var result = A.clone(); + return result.div(B); + } + + function diag(A){ + var diag = null; + var rows = A.rows, cols = A.columns, j, r; + //It is an array + if(typeof cols === "undefined" && (typeof A)=='object'){ + if(A[0]&&A[0].length){ + rows = A.length; + cols = A[0].length; + r = Math.min(rows,cols); + diag = Matrix.zeros(cols, cols); + for (j = 0; j < cols; j++) { + diag[j][j]=A[j][j]; + } + } + else{ + cols = A.length; + diag = Matrix.zeros(cols, cols); + for (j = 0; j < cols; j++) { + diag[j][j]=A[j]; + } + } + + } + if(rows == 1){ + diag = Matrix.zeros(cols, cols); + for (j = 0; j < cols; j++) { + diag[j][j]=A[0][j]; + } + } + else{ + if(rows>0 && cols > 0){ + r = Math.min(rows,cols); + diag = new Array(r); + for (j = 0; j < r; j++) { + diag[j] = A[j][j]; + } + } + } + return diag; + } + + function min(A, B){ + if(typeof A==='number' && typeof B ==='number') + return Math.min(A,B); + var ii = A.rows, jj = A.columns; + var result = new Matrix(ii,jj); + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + if (A[i][j] < B[i][j]) { + result[i][j] = A[i][j]; + } + else{ + result[i][j] = B[i][j]; + } + } + } + return result; + } + + function max(A, B){ + if(typeof A==='number' && typeof B ==='number') + return Math.max(A,B); + var ii = A.rows, jj = A.columns; + var result = new Matrix(ii,jj); + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + if (A[i][j] > B[i][j]) { + result[i][j] = A[i][j]; + } + else{ + result[i][j] = B[i][j]; + } + } + } + return result; + } + + function sqrt(A){ + if(typeof A==='number' ) + return Math.sqrt(A); + var ii = A.rows, jj = A.columns; + var result = new Matrix(ii,jj); + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + result[i][j] = Math.sqrt(A[i][j]); + + } + } + return result; + } + + function abs(A){ + if(typeof A==='number' ) + return Math.abs(A); + var ii = A.rows, jj = A.columns; + var result = new Matrix(ii,jj); + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + result[i][j] = Math.abs(A[i][j]); + + } + } + return result; + } + + function exp(A){ + if(typeof A==='number' ) + return Math.sqrt(A); + var ii = A.rows, jj = A.columns; + var result = new Matrix(ii,jj); + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + result[i][j] = Math.exp(A[i][j]); + } + } + return result; + } + + function dotPow(A, b){ + if(typeof A==='number' ) + return Math.pow(A,b); + //console.log(A); + var ii = A.rows, jj = A.columns; + var result = new Matrix(ii,jj); + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + result[i][j] = Math.pow(A[i][j],b); + } + } + return result; + } + + function solve(A, B){ + return A.solve(B); + } + + function inv(A){ + if(typeof A ==="number") + return 1/A; + return A.inverse(); + } + + module.exports = { + transpose:transpose, + add:add, + subtract:subtract, + multiply:multiply, + dotMultiply:dotMultiply, + dotDivide:dotDivide, + diag:diag, + min:min, + max:max, + solve:solve, + inv:inv, + sqrt:sqrt, + exp:exp, + dotPow:dotPow, + abs:abs, + matrix:matrix, + ones:ones, + zeros:zeros, + random:random, + eye:eye + }; + + +/***/ }, +/* 113 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + module.exports = __webpack_require__(114); + module.exports.Decompositions = module.exports.DC = __webpack_require__(115); + + +/***/ }, +/* 114 */ +/***/ function(module, exports) { + + 'use strict'; + + var Asplice = Array.prototype.splice, + Aconcat = Array.prototype.concat; + + // For performance : http://jsperf.com/clone-array-slice-vs-while-vs-for + function slice(arr) { + var i = 0, + ii = arr.length, + result = new Array(ii); + for (; i < ii; i++) { + result[i] = arr[i]; + } + return result; + } + + /** + * Real matrix. + * @constructor + * @param {number|Array} nRows - Number of rows of the new matrix or a 2D array containing the data. + * @param {number|boolean} [nColumns] - Number of columns of the new matrix or a boolean specifying if the input array should be cloned + */ + function Matrix(nRows, nColumns) { + var i = 0, rows, columns, matrix, newInstance; + if (Array.isArray(nRows)) { + newInstance = nColumns; + matrix = newInstance ? slice(nRows) : nRows; + nRows = matrix.length; + nColumns = matrix[0].length; + if (typeof nColumns === 'undefined') { + throw new TypeError('Data must be a 2D array'); + } + if (nRows > 0 && nColumns > 0) { + for (; i < nRows; i++) { + if (matrix[i].length !== nColumns) { + throw new RangeError('Inconsistent array dimensions'); + } else if (newInstance) { + matrix[i] = slice(matrix[i]); + } + } + } else { + throw new RangeError('Invalid dimensions: ' + nRows + 'x' + nColumns); + } + } else if (typeof nRows === 'number') { // Create empty matrix + if (nRows > 0 && nColumns > 0) { + matrix = new Array(nRows); + for (; i < nRows; i++) { + matrix[i] = new Array(nColumns); + } + } else { + throw new RangeError('Invalid dimensions: ' + nRows + 'x' + nColumns); + } + } else { + throw new TypeError('Invalid arguments'); + } + + Object.defineProperty(matrix, 'rows', {writable: true, value: nRows}); + Object.defineProperty(matrix, 'columns', {writable: true, value: nColumns}); + + matrix.__proto__ = Matrix.prototype; + + return matrix; + } + + /** + * Constructs a Matrix with the chosen dimensions from a 1D array. + * @param {number} newRows - Number of rows + * @param {number} newColumns - Number of columns + * @param {Array} newData - A 1D array containing data for the matrix + * @returns {Matrix} - The new matrix + */ + Matrix.from1DArray = function from1DArray(newRows, newColumns, newData) { + var length, data, i = 0; + + length = newRows * newColumns; + if (length !== newData.length) + throw new RangeError('Data length does not match given dimensions'); + + data = new Array(newRows); + for (; i < newRows; i++) { + data[i] = newData.slice(i * newColumns, (i + 1) * newColumns); + } + return new Matrix(data); + }; + + /** + * Creates a row vector, a matrix with only one row. + * @param {Array} newData - A 1D array containing data for the vector + * @returns {Matrix} - The new matrix + */ + Matrix.rowVector = function rowVector(newData) { + return new Matrix([newData]); + }; + + /** + * Creates a column vector, a matrix with only one column. + * @param {Array} newData - A 1D array containing data for the vector + * @returns {Matrix} - The new matrix + */ + Matrix.columnVector = function columnVector(newData) { + var l = newData.length, vector = new Array(l); + for (var i = 0; i < l; i++) + vector[i] = [newData[i]]; + return new Matrix(vector); + }; + + /** + * Creates an empty matrix with the given dimensions. Values will be undefined. Same as using new Matrix(rows, columns). + * @param {number} rows - Number of rows + * @param {number} columns - Number of columns + * @returns {Matrix} - The new matrix + */ + Matrix.empty = function empty(rows, columns) { + return new Matrix(rows, columns); + }; + + /** + * Creates a matrix with the given dimensions. Values will be set to zero. + * @param {number} rows - Number of rows + * @param {number} columns - Number of columns + * @returns {Matrix} - The new matrix + */ + Matrix.zeros = function zeros(rows, columns) { + return Matrix.empty(rows, columns).fill(0); + }; + + /** + * Creates a matrix with the given dimensions. Values will be set to one. + * @param {number} rows - Number of rows + * @param {number} columns - Number of columns + * @returns {Matrix} - The new matrix + */ + Matrix.ones = function ones(rows, columns) { + return Matrix.empty(rows, columns).fill(1); + }; + + /** + * Creates a matrix with the given dimensions. Values will be randomly set using Math.random(). + * @param {number} rows - Number of rows + * @param {number} columns - Number of columns + * @returns {Matrix} The new matrix + */ + Matrix.rand = function rand(rows, columns) { + var matrix = Matrix.empty(rows, columns); + for (var i = 0, ii = matrix.rows; i < ii; i++) { + for (var j = 0, jj = matrix.columns; j < jj; j++) { + matrix[i][j] = Math.random(); + } + } + return matrix; + }; + + /** + * Creates an identity matrix with the given dimension. Values of the diagonal will be 1 and other will be 0. + * @param {number} n - Number of rows and columns + * @returns {Matrix} - The new matrix + */ + Matrix.eye = function eye(n) { + var matrix = Matrix.zeros(n, n), l = matrix.rows; + for (var i = 0; i < l; i++) { + matrix[i][i] = 1; + } + return matrix; + }; + + /** + * Creates a diagonal matrix based on the given array. + * @param {Array} data - Array containing the data for the diagonal + * @returns {Matrix} - The new matrix + */ + Matrix.diag = function diag(data) { + var l = data.length, matrix = Matrix.zeros(l, l); + for (var i = 0; i < l; i++) { + matrix[i][i] = data[i]; + } + return matrix; + }; + + /** + * Creates an array of indices between two values + * @param {number} from + * @param {number} to + * @returns {Array} + */ + Matrix.indices = function indices(from, to) { + var vector = new Array(to - from); + for (var i = 0; i < vector.length; i++) + vector[i] = from++; + return vector; + }; + + // TODO DOC + Matrix.stack = function stack(arg1) { + var i, j, k; + if (Matrix.isMatrix(arg1)) { + var rows = 0, + cols = 0; + for (i = 0; i < arguments.length; i++) { + rows += arguments[i].rows; + if (arguments[i].columns > cols) + cols = arguments[i].columns; + } + + var r = Matrix.zeros(rows, cols); + var c = 0; + for (i = 0; i < arguments.length; i++) { + var current = arguments[i]; + for (j = 0; j < current.rows; j++) { + for (k = 0; k < current.columns; k++) + r[c][k] = current[j][k]; + c++; + } + } + return r; + } + else if (Array.isArray(arg1)) { + var matrix = Matrix.empty(arguments.length, arg1.length); + for (i = 0; i < arguments.length; i++) + matrix.setRow(i, arguments[i]); + return matrix; + } + }; + + // TODO DOC + Matrix.expand = function expand(base, count) { + var expansion = []; + for (var i = 0; i < count.length; i++) + for (var j = 0; j < count[i]; j++) + expansion.push(base[i]); + return new Matrix(expansion); + }; + + /** + * Check that the provided value is a Matrix and tries to instantiate one if not + * @param value - The value to check + * @returns {Matrix} + * @throws {TypeError} + */ + Matrix.checkMatrix = function checkMatrix(value) { + if (!value) { + throw new TypeError('Argument has to be a matrix'); + } + if (value.klass !== 'Matrix') { + value = new Matrix(value); + } + return value; + }; + + /** + * Returns true if the argument is a Matrix, false otherwise + * @param value - The value to check + * @returns {boolean} + */ + Matrix.isMatrix = function isMatrix(value) { + return value ? value.klass === 'Matrix' : false; + }; + + /** + * @property {string} - The name of this class. + */ + Object.defineProperty(Matrix.prototype, 'klass', { + get: function klass() { + return 'Matrix'; + } + }); + + /** + * @property {number} - The number of elements in the matrix. + */ + Object.defineProperty(Matrix.prototype, 'size', { + get: function size() { + return this.rows * this.columns; + } + }); + + /** + * @private + * Internal check that a row index is not out of bounds + * @param {number} index + */ + Matrix.prototype.checkRowIndex = function checkRowIndex(index) { + if (index < 0 || index > this.rows - 1) + throw new RangeError('Row index out of range.'); + }; + + /** + * @private + * Internal check that a column index is not out of bounds + * @param {number} index + */ + Matrix.prototype.checkColumnIndex = function checkColumnIndex(index) { + if (index < 0 || index > this.columns - 1) + throw new RangeError('Column index out of range.'); + }; + + /** + * @private + * Internal check that two matrices have the same dimensions + * @param {Matrix} otherMatrix + */ + Matrix.prototype.checkDimensions = function checkDimensions(otherMatrix) { + if ((this.rows !== otherMatrix.rows) || (this.columns !== otherMatrix.columns)) + throw new RangeError('Matrices dimensions must be equal.'); + }; + + /** + * Applies a callback for each element of the matrix. The function is called in the matrix (this) context. + * @param {function} callback - Function that will be called with two parameters : i (row) and j (column) + * @returns {Matrix} this + */ + Matrix.prototype.apply = function apply(callback) { + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + callback.call(this, i, j); + } + } + return this; + }; + + /** + * Creates an exact and independent copy of the matrix + * @returns {Matrix} + */ + Matrix.prototype.clone = function clone() { + return new Matrix(this.to2DArray()); + }; + + /** + * Returns a new 1D array filled row by row with the matrix values + * @returns {Array} + */ + Matrix.prototype.to1DArray = function to1DArray() { + return Aconcat.apply([], this); + }; + + /** + * Returns a 2D array containing a copy of the data + * @returns {Array} + */ + Matrix.prototype.to2DArray = function to2DArray() { + var l = this.rows, copy = new Array(l); + for (var i = 0; i < l; i++) { + copy[i] = slice(this[i]); + } + return copy; + }; + + /** + * @returns {boolean} true if the matrix has one row + */ + Matrix.prototype.isRowVector = function isRowVector() { + return this.rows === 1; + }; + + /** + * @returns {boolean} true if the matrix has one column + */ + Matrix.prototype.isColumnVector = function isColumnVector() { + return this.columns === 1; + }; + + /** + * @returns {boolean} true if the matrix has one row or one column + */ + Matrix.prototype.isVector = function isVector() { + return (this.rows === 1) || (this.columns === 1); + }; + + /** + * @returns {boolean} true if the matrix has the same number of rows and columns + */ + Matrix.prototype.isSquare = function isSquare() { + return this.rows === this.columns; + }; + + /** + * @returns {boolean} true if the matrix is square and has the same values on both sides of the diagonal + */ + Matrix.prototype.isSymmetric = function isSymmetric() { + if (this.isSquare()) { + var l = this.rows; + for (var i = 0; i < l; i++) { + for (var j = 0; j <= i; j++) { + if (this[i][j] !== this[j][i]) { + return false; + } + } + } + return true; + } + return false; + }; + + /** + * Sets a given element of the matrix. mat.set(3,4,1) is equivalent to mat[3][4]=1 + * @param {number} rowIndex - Index of the row + * @param {number} columnIndex - Index of the column + * @param {number} value - The new value for the element + * @returns {Matrix} this + */ + Matrix.prototype.set = function set(rowIndex, columnIndex, value) { + this[rowIndex][columnIndex] = value; + return this; + }; + + /** + * Returns the given element of the matrix. mat.get(3,4) is equivalent to matrix[3][4] + * @param {number} rowIndex - Index of the row + * @param {number} columnIndex - Index of the column + * @returns {number} + */ + Matrix.prototype.get = function get(rowIndex, columnIndex) { + return this[rowIndex][columnIndex]; + }; + + /** + * Fills the matrix with a given value. All elements will be set to this value. + * @param {number} value - New value + * @returns {Matrix} this + */ + Matrix.prototype.fill = function fill(value) { + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + this[i][j] = value; + } + } + return this; + }; + + /** + * Negates the matrix. All elements will be multiplied by (-1) + * @returns {Matrix} this + */ + Matrix.prototype.neg = function neg() { + return this.mulS(-1); + }; + + /** + * Adds a scalar or values from another matrix (in place) + * @param {number|Matrix} value + * @returns {Matrix} this + */ + Matrix.prototype.add = function add(value) { + if (typeof value === 'number') + return this.addS(value); + value = Matrix.checkMatrix(value); + return this.addM(value); + }; + + /** + * Adds a scalar to each element of the matrix + * @param {number} value + * @returns {Matrix} this + */ + Matrix.prototype.addS = function addS(value) { + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + this[i][j] += value; + } + } + return this; + }; + + /** + * Adds the value of each element of matrix to the corresponding element of this + * @param {Matrix} matrix + * @returns {Matrix} this + */ + Matrix.prototype.addM = function addM(matrix) { + this.checkDimensions(matrix); + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + this[i][j] += matrix[i][j]; + } + } + return this; + }; + + /** + * Subtracts a scalar or values from another matrix (in place) + * @param {number|Matrix} value + * @returns {Matrix} this + */ + Matrix.prototype.sub = function sub(value) { + if (typeof value === 'number') + return this.subS(value); + value = Matrix.checkMatrix(value); + return this.subM(value); + }; + + /** + * Subtracts a scalar from each element of the matrix + * @param {number} value + * @returns {Matrix} this + */ + Matrix.prototype.subS = function subS(value) { + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + this[i][j] -= value; + } + } + return this; + }; + + /** + * Subtracts the value of each element of matrix from the corresponding element of this + * @param {Matrix} matrix + * @returns {Matrix} this + */ + Matrix.prototype.subM = function subM(matrix) { + this.checkDimensions(matrix); + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + this[i][j] -= matrix[i][j]; + } + } + return this; + }; + + /** + * Multiplies a scalar or values from another matrix (in place) + * @param {number|Matrix} value + * @returns {Matrix} this + */ + Matrix.prototype.mul = function mul(value) { + if (typeof value === 'number') + return this.mulS(value); + value = Matrix.checkMatrix(value); + return this.mulM(value); + }; + + /** + * Multiplies a scalar with each element of the matrix + * @param {number} value + * @returns {Matrix} this + */ + Matrix.prototype.mulS = function mulS(value) { + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + this[i][j] *= value; + } + } + return this; + }; + + /** + * Multiplies the value of each element of matrix with the corresponding element of this + * @param {Matrix} matrix + * @returns {Matrix} this + */ + Matrix.prototype.mulM = function mulM(matrix) { + this.checkDimensions(matrix); + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + this[i][j] *= matrix[i][j]; + } + } + return this; + }; + + /** + * Divides by a scalar or values from another matrix (in place) + * @param {number|Matrix} value + * @returns {Matrix} this + */ + Matrix.prototype.div = function div(value) { + if (typeof value === 'number') + return this.divS(value); + value = Matrix.checkMatrix(value); + return this.divM(value); + }; + + /** + * Divides each element of the matrix by a scalar + * @param {number} value + * @returns {Matrix} this + */ + Matrix.prototype.divS = function divS(value) { + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + this[i][j] /= value; + } + } + return this; + }; + + /** + * Divides each element of this by the corresponding element of matrix + * @param {Matrix} matrix + * @returns {Matrix} this + */ + Matrix.prototype.divM = function divM(matrix) { + this.checkDimensions(matrix); + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + this[i][j] /= matrix[i][j]; + } + } + return this; + }; + + /** + * Returns a new array from the given row index + * @param {number} index - Row index + * @returns {Array} + */ + Matrix.prototype.getRow = function getRow(index) { + this.checkRowIndex(index); + return slice(this[index]); + }; + + /** + * Returns a new row vector from the given row index + * @param {number} index - Row index + * @returns {Matrix} + */ + Matrix.prototype.getRowVector = function getRowVector(index) { + return Matrix.rowVector(this.getRow(index)); + }; + + /** + * Sets a row at the given index + * @param {number} index - Row index + * @param {Array|Matrix} array - Array or vector + * @returns {Matrix} this + */ + Matrix.prototype.setRow = function setRow(index, array) { + this.checkRowIndex(index); + if (Matrix.isMatrix(array)) array = array.to1DArray(); + if (array.length !== this.columns) + throw new RangeError('Invalid row size'); + this[index] = slice(array); + return this; + }; + + /** + * Removes a row from the given index + * @param {number} index - Row index + * @returns {Matrix} this + */ + Matrix.prototype.removeRow = function removeRow(index) { + this.checkRowIndex(index); + if (this.rows === 1) + throw new RangeError('A matrix cannot have less than one row'); + Asplice.call(this, index, 1); + this.rows -= 1; + return this; + }; + + /** + * Adds a row at the given index + * @param {number} [index = this.rows] - Row index + * @param {Array|Matrix} array - Array or vector + * @returns {Matrix} this + */ + Matrix.prototype.addRow = function addRow(index, array) { + if (typeof array === 'undefined') { + array = index; + index = this.rows; + } + if (index < 0 || index > this.rows) + throw new RangeError('Row index out of range.'); + if (Matrix.isMatrix(array)) array = array.to1DArray(); + if (array.length !== this.columns) + throw new RangeError('Invalid row size'); + Asplice.call(this, index, 0, slice(array)); + this.rows += 1; + return this; + }; + + /** + * Swaps two rows + * @param {number} row1 - First row index + * @param {number} row2 - Second row index + * @returns {Matrix} this + */ + Matrix.prototype.swapRows = function swapRows(row1, row2) { + this.checkRowIndex(row1); + this.checkRowIndex(row2); + var temp = this[row1]; + this[row1] = this[row2]; + this[row2] = temp; + return this; + }; + + /** + * Returns a new array from the given column index + * @param {number} index - Column index + * @returns {Array} + */ + Matrix.prototype.getColumn = function getColumn(index) { + this.checkColumnIndex(index); + var l = this.rows, column = new Array(l); + for (var i = 0; i < l; i++) { + column[i] = this[i][index]; + } + return column; + }; + + /** + * Returns a new column vector from the given column index + * @param {number} index - Column index + * @returns {Matrix} + */ + Matrix.prototype.getColumnVector = function getColumnVector(index) { + return Matrix.columnVector(this.getColumn(index)); + }; + + /** + * Sets a column at the given index + * @param {number} index - Column index + * @param {Array|Matrix} array - Array or vector + * @returns {Matrix} this + */ + Matrix.prototype.setColumn = function setColumn(index, array) { + this.checkColumnIndex(index); + if (Matrix.isMatrix(array)) array = array.to1DArray(); + var l = this.rows; + if (array.length !== l) + throw new RangeError('Invalid column size'); + for (var i = 0; i < l; i++) { + this[i][index] = array[i]; + } + return this; + }; + + /** + * Removes a column from the given index + * @param {number} index - Column index + * @returns {Matrix} this + */ + Matrix.prototype.removeColumn = function removeColumn(index) { + this.checkColumnIndex(index); + if (this.columns === 1) + throw new RangeError('A matrix cannot have less than one column'); + for (var i = 0, ii = this.rows; i < ii; i++) { + this[i].splice(index, 1); + } + this.columns -= 1; + return this; + }; + + /** + * Adds a column at the given index + * @param {number} [index = this.columns] - Column index + * @param {Array|Matrix} array - Array or vector + * @returns {Matrix} this + */ + Matrix.prototype.addColumn = function addColumn(index, array) { + if (typeof array === 'undefined') { + array = index; + index = this.columns; + } + if (index < 0 || index > this.columns) + throw new RangeError('Column index out of range.'); + if (Matrix.isMatrix(array)) array = array.to1DArray(); + var l = this.rows; + if (array.length !== l) + throw new RangeError('Invalid column size'); + for (var i = 0; i < l; i++) { + this[i].splice(index, 0, array[i]); + } + this.columns += 1; + return this; + }; + + /** + * Swaps two columns + * @param {number} column1 - First column index + * @param {number} column2 - Second column index + * @returns {Matrix} this + */ + Matrix.prototype.swapColumns = function swapColumns(column1, column2) { + this.checkRowIndex(column1); + this.checkRowIndex(column2); + var l = this.rows, temp, row; + for (var i = 0; i < l; i++) { + row = this[i]; + temp = row[column1]; + row[column1] = row[column2]; + row[column2] = temp; + } + return this; + }; + + /** + * @private + * Internal check that the provided vector is an array with the right length + * @param {Array|Matrix} vector + * @returns {Array} + * @throws {RangeError} + */ + Matrix.prototype.checkRowVector = function checkRowVector(vector) { + if (Matrix.isMatrix(vector)) + vector = vector.to1DArray(); + if (vector.length !== this.columns) + throw new RangeError('vector size must be the same as the number of columns'); + return vector; + }; + + /** + * @private + * Internal check that the provided vector is an array with the right length + * @param {Array|Matrix} vector + * @returns {Array} + * @throws {RangeError} + */ + Matrix.prototype.checkColumnVector = function checkColumnVector(vector) { + if (Matrix.isMatrix(vector)) + vector = vector.to1DArray(); + if (vector.length !== this.rows) + throw new RangeError('vector size must be the same as the number of rows'); + return vector; + }; + + /** + * Adds the values of a vector to each row + * @param {Array|Matrix} vector - Array or vector + * @returns {Matrix} this + */ + Matrix.prototype.addRowVector = function addRowVector(vector) { + vector = this.checkRowVector(vector); + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + this[i][j] += vector[j]; + } + } + return this; + }; + + /** + * Subtracts the values of a vector from each row + * @param {Array|Matrix} vector - Array or vector + * @returns {Matrix} this + */ + Matrix.prototype.subRowVector = function subRowVector(vector) { + vector = this.checkRowVector(vector); + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + this[i][j] -= vector[j]; + } + } + return this; + }; + + /** + * Multiplies the values of a vector with each row + * @param {Array|Matrix} vector - Array or vector + * @returns {Matrix} this + */ + Matrix.prototype.mulRowVector = function mulRowVector(vector) { + vector = this.checkRowVector(vector); + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + this[i][j] *= vector[j]; + } + } + return this; + }; + + /** + * Divides the values of each row by those of a vector + * @param {Array|Matrix} vector - Array or vector + * @returns {Matrix} this + */ + Matrix.prototype.divRowVector = function divRowVector(vector) { + vector = this.checkRowVector(vector); + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + this[i][j] /= vector[j]; + } + } + return this; + }; + + /** + * Adds the values of a vector to each column + * @param {Array|Matrix} vector - Array or vector + * @returns {Matrix} this + */ + Matrix.prototype.addColumnVector = function addColumnVector(vector) { + vector = this.checkColumnVector(vector); + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + this[i][j] += vector[i]; + } + } + return this; + }; + + /** + * Subtracts the values of a vector from each column + * @param {Array|Matrix} vector - Array or vector + * @returns {Matrix} this + */ + Matrix.prototype.subColumnVector = function subColumnVector(vector) { + vector = this.checkColumnVector(vector); + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + this[i][j] -= vector[i]; + } + } + return this; + }; + + /** + * Multiplies the values of a vector with each column + * @param {Array|Matrix} vector - Array or vector + * @returns {Matrix} this + */ + Matrix.prototype.mulColumnVector = function mulColumnVector(vector) { + vector = this.checkColumnVector(vector); + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + this[i][j] *= vector[i]; + } + } + return this; + }; + + /** + * Divides the values of each column by those of a vector + * @param {Array|Matrix} vector - Array or vector + * @returns {Matrix} this + */ + Matrix.prototype.divColumnVector = function divColumnVector(vector) { + vector = this.checkColumnVector(vector); + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + this[i][j] /= vector[i]; + } + } + return this; + }; + + /** + * Multiplies the values of a row with a scalar + * @param {number} index - Row index + * @param {number} value + * @returns {Matrix} this + */ + Matrix.prototype.mulRow = function mulRow(index, value) { + this.checkRowIndex(index); + var i = 0, l = this.columns; + for (; i < l; i++) { + this[index][i] *= value; + } + return this; + }; + + /** + * Multiplies the values of a column with a scalar + * @param {number} index - Column index + * @param {number} value + * @returns {Matrix} this + */ + Matrix.prototype.mulColumn = function mulColumn(index, value) { + this.checkColumnIndex(index); + var i = 0, l = this.rows; + for (; i < l; i++) { + this[i][index] *= value; + } + }; + + /** + * A matrix index + * @typedef {Object} MatrixIndex + * @property {number} row + * @property {number} column + */ + + /** + * Returns the maximum value of the matrix + * @returns {number} + */ + Matrix.prototype.max = function max() { + var v = -Infinity; + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + if (this[i][j] > v) { + v = this[i][j]; + } + } + } + return v; + }; + + /** + * Returns the index of the maximum value + * @returns {MatrixIndex} + */ + Matrix.prototype.maxIndex = function maxIndex() { + var v = -Infinity; + var idx = {}; + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + if (this[i][j] > v) { + v = this[i][j]; + idx.row = i; + idx.column = j; + } + } + } + return idx; + }; + + /** + * Returns the minimum value of the matrix + * @returns {number} + */ + Matrix.prototype.min = function min() { + var v = Infinity; + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + if (this[i][j] < v) { + v = this[i][j]; + } + } + } + return v; + }; + + /** + * Returns the index of the minimum value + * @returns {MatrixIndex} + */ + Matrix.prototype.minIndex = function minIndex() { + var v = Infinity; + var idx = {}; + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + if (this[i][j] < v) { + v = this[i][j]; + idx.row = i; + idx.column = j; + } + } + } + return idx; + }; + + /** + * Returns the maximum value of one row + * @param {number} index - Row index + * @returns {number} + */ + Matrix.prototype.maxRow = function maxRow(index) { + this.checkRowIndex(index); + var v = -Infinity; + for (var i = 0, ii = this.columns; i < ii; i++) { + if (this[index][i] > v) { + v = this[index][i]; + } + } + return v; + }; + + /** + * Returns the index of the maximum value of one row + * @param {number} index - Row index + * @returns {MatrixIndex} + */ + Matrix.prototype.maxRowIndex = function maxRowIndex(index) { + this.checkRowIndex(index); + var v = -Infinity; + var idx = { + row: index + }; + for (var i = 0, ii = this.columns; i < ii; i++) { + if (this[index][i] > v) { + v = this[index][i]; + idx.column = i; + } + } + return idx; + }; + + /** + * Returns the minimum value of one row + * @param {number} index - Row index + * @returns {number} + */ + Matrix.prototype.minRow = function minRow(index) { + this.checkRowIndex(index); + var v = Infinity; + for (var i = 0, ii = this.columns; i < ii; i++) { + if (this[index][i] < v) { + v = this[index][i]; + } + } + return v; + }; + + /** + * Returns the index of the maximum value of one row + * @param {number} index - Row index + * @returns {MatrixIndex} + */ + Matrix.prototype.minRowIndex = function minRowIndex(index) { + this.checkRowIndex(index); + var v = Infinity; + var idx = { + row: index, + column: 0 + }; + for (var i = 0, ii = this.columns; i < ii; i++) { + if (this[index][i] < v) { + v = this[index][i]; + idx.column = i; + } + } + return idx; + }; + + /** + * Returns the maximum value of one column + * @param {number} index - Column index + * @returns {number} + */ + Matrix.prototype.maxColumn = function maxColumn(index) { + this.checkColumnIndex(index); + var v = -Infinity; + for (var i = 0, ii = this.rows; i < ii; i++) { + if (this[i][index] > v) { + v = this[i][index]; + } + } + return v; + }; + + /** + * Returns the index of the maximum value of one column + * @param {number} index - Column index + * @returns {MatrixIndex} + */ + Matrix.prototype.maxColumnIndex = function maxColumnIndex(index) { + this.checkColumnIndex(index); + var v = -Infinity; + var idx = { + row: 0, + column: index + }; + for (var i = 0, ii = this.rows; i < ii; i++) { + if (this[i][index] > v) { + v = this[i][index]; + idx.row = i; + } + } + return idx; + }; + + /** + * Returns the minimum value of one column + * @param {number} index - Column index + * @returns {number} + */ + Matrix.prototype.minColumn = function minColumn(index) { + this.checkColumnIndex(index); + var v = Infinity; + for (var i = 0, ii = this.rows; i < ii; i++) { + if (this[i][index] < v) { + v = this[i][index]; + } + } + return v; + }; + + /** + * Returns the index of the minimum value of one column + * @param {number} index - Column index + * @returns {MatrixIndex} + */ + Matrix.prototype.minColumnIndex = function minColumnIndex(index) { + this.checkColumnIndex(index); + var v = Infinity; + var idx = { + row: 0, + column: index + }; + for (var i = 0, ii = this.rows; i < ii; i++) { + if (this[i][index] < v) { + v = this[i][index]; + idx.row = i; + } + } + return idx; + }; + + /** + * Returns an array containing the diagonal values of the matrix + * @returns {Array} + */ + Matrix.prototype.diag = function diag() { + if (!this.isSquare()) + throw new TypeError('Only square matrices have a diagonal.'); + var diag = new Array(this.rows); + for (var i = 0, ii = this.rows; i < ii; i++) { + diag[i] = this[i][i]; + } + return diag; + }; + + /** + * Returns the sum of all elements of the matrix + * @returns {number} + */ + Matrix.prototype.sum = function sum() { + var v = 0; + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + v += this[i][j]; + } + } + return v; + }; + + /** + * Returns the mean of all elements of the matrix + * @returns {number} + */ + Matrix.prototype.mean = function mean() { + return this.sum() / this.size; + }; + + /** + * Returns the product of all elements of the matrix + * @returns {number} + */ + Matrix.prototype.prod = function prod() { + var prod = 1; + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + prod *= this[i][j]; + } + } + return prod; + }; + + /** + * Computes the cumulative sum of the matrix elements (in place, row by row) + * @returns {Matrix} this + */ + Matrix.prototype.cumulativeSum = function cumulativeSum() { + var sum = 0; + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + sum += this[i][j]; + this[i][j] = sum; + } + } + return this; + }; + + /** + * Computes the dot (scalar) product between the matrix and another + * @param {Matrix} other vector + * @returns {number} + */ + Matrix.prototype.dot = function dot(other) { + if (this.size !== other.size) + throw new RangeError('vectors do not have the same size'); + var vector1 = this.to1DArray(); + var vector2 = other.to1DArray(); + var dot = 0, l = vector1.length; + for (var i = 0; i < l; i++) { + dot += vector1[i] * vector2[i]; + } + return dot; + }; + + /** + * Returns the matrix product between this and other + * @returns {Matrix} + */ + Matrix.prototype.mmul = function mmul(other) { + if (!Matrix.isMatrix(other)) + throw new TypeError('parameter "other" must be a matrix'); + if (this.columns !== other.rows) + console.warn('Number of columns of left matrix are not equal to number of rows of right matrix.'); + + var m = this.rows, n = this.columns, p = other.columns; + var result = new Matrix(m, p); + + var Bcolj = new Array(n); + var i, j, k; + for (j = 0; j < p; j++) { + for (k = 0; k < n; k++) + Bcolj[k] = other[k][j]; + + for (i = 0; i < m; i++) { + var Arowi = this[i]; + + var s = 0; + for (k = 0; k < n; k++) + s += Arowi[k] * Bcolj[k]; + + result[i][j] = s; + } + } + return result; + }; + + /** + * Sorts the rows (in place) + * @param {function} compareFunction - usual Array.prototype.sort comparison function + * @returns {Matrix} this + */ + Matrix.prototype.sortRows = function sortRows(compareFunction) { + for (var i = 0, ii = this.rows; i < ii; i++) { + this[i].sort(compareFunction); + } + return this; + }; + + /** + * Sorts the columns (in place) + * @param {function} compareFunction - usual Array.prototype.sort comparison function + * @returns {Matrix} this + */ + Matrix.prototype.sortColumns = function sortColumns(compareFunction) { + for (var i = 0, ii = this.columns; i < ii; i++) { + this.setColumn(i, this.getColumn(i).sort(compareFunction)); + } + return this; + }; + + /** + * Transposes the matrix and returns a new one containing the result + * @returns {Matrix} + */ + Matrix.prototype.transpose = function transpose() { + var result = new Matrix(this.columns, this.rows); + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + result[j][i] = this[i][j]; + } + } + return result; + }; + + /** + * Returns a subset of the matrix + * @param {number} startRow - First row index + * @param {number} endRow - Last row index + * @param {number} startColumn - First column index + * @param {number} endColumn - Last column index + * @returns {Matrix} + */ + Matrix.prototype.subMatrix = function subMatrix(startRow, endRow, startColumn, endColumn) { + if ((startRow > endRow) || (startColumn > endColumn) || (startRow < 0) || (startRow >= this.rows) || (endRow < 0) || (endRow >= this.rows) || (startColumn < 0) || (startColumn >= this.columns) || (endColumn < 0) || (endColumn >= this.columns)) + throw new RangeError('Argument out of range'); + var newMatrix = new Matrix(endRow - startRow + 1, endColumn - startColumn + 1); + for (var i = startRow; i <= endRow; i++) { + for (var j = startColumn; j <= endColumn; j++) { + newMatrix[i - startRow][j - startColumn] = this[i][j]; + } + } + return newMatrix; + }; + + /** + * Returns a subset of the matrix based on an array of row indices + * @param {Array} indices - Array containing the row indices + * @param {number} [startColumn = 0] - First column index + * @param {number} [endColumn = this.columns-1] - Last column index + * @returns {Matrix} + */ + Matrix.prototype.subMatrixRow = function subMatrixRow(indices, startColumn, endColumn) { + if (typeof startColumn === 'undefined') { + startColumn = 0; + endColumn = this.columns - 1; + } else if (typeof endColumn === 'undefined') { + endColumn = this.columns - 1; + } + if ((startColumn > endColumn) || (startColumn < 0) || (startColumn >= this.columns) || (endColumn < 0) || (endColumn >= this.columns)) + throw new RangeError('Argument out of range.'); + var l = indices.length, rows = this.rows, + X = new Matrix(l, endColumn - startColumn + 1); + for (var i = 0; i < l; i++) { + for (var j = startColumn; j <= endColumn; j++) { + if ((indices[i] < 0) || (indices[i] >= rows)) + throw new RangeError('Argument out of range.'); + X[i][j - startColumn] = this[indices[i]][j]; + } + } + return X; + }; + + /** + * Returns a subset of the matrix based on an array of column indices + * @param {Array} indices - Array containing the column indices + * @param {number} [startRow = 0] - First row index + * @param {number} [endRow = this.rows-1] - Last row index + * @returns {Matrix} + */ + Matrix.prototype.subMatrixColumn = function subMatrixColumn(indices, startRow, endRow) { + if (typeof startRow === 'undefined') { + startRow = 0; + endRow = this.rows - 1; + } else if (typeof endRow === 'undefined') { + endRow = this.rows - 1; + } + if ((startRow > endRow) || (startRow < 0) || (startRow >= this.rows) || (endRow < 0) || (endRow >= this.rows)) + throw new RangeError('Argument out of range.'); + var l = indices.length, columns = this.columns, + X = new Matrix(endRow - startRow + 1, l); + for (var i = 0; i < l; i++) { + for (var j = startRow; j <= endRow; j++) { + if ((indices[i] < 0) || (indices[i] >= columns)) + throw new RangeError('Argument out of range.'); + X[j - startRow][i] = this[j][indices[i]]; + } + } + return X; + }; + + /** + * Returns the trace of the matrix (sum of the diagonal elements) + * @returns {number} + */ + Matrix.prototype.trace = function trace() { + if (!this.isSquare()) + throw new TypeError('The matrix is not square'); + var trace = 0, i = 0, l = this.rows; + for (; i < l; i++) { + trace += this[i][i]; + } + return trace; + }; + + /** + * Sets each element of the matrix to its absolute value + * @returns {Matrix} this + */ + Matrix.prototype.abs = function abs() { + var ii = this.rows, jj = this.columns; + for (var i = 0; i < ii; i++) { + for (var j = 0; j < jj; j++) { + this[i][j] = Math.abs(this[i][j]); + } + } + }; + + module.exports = Matrix; + + +/***/ }, +/* 115 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var Matrix = __webpack_require__(114); + + var SingularValueDecomposition = __webpack_require__(116); + var EigenvalueDecomposition = __webpack_require__(118); + var LuDecomposition = __webpack_require__(119); + var QrDecomposition = __webpack_require__(120); + var CholeskyDecomposition = __webpack_require__(121); + + function inverse(matrix) { + return solve(matrix, Matrix.eye(matrix.rows)); + } + + Matrix.prototype.inverse = function () { + return inverse(this); + }; + + function solve(leftHandSide, rightHandSide) { + return leftHandSide.isSquare() ? new LuDecomposition(leftHandSide).solve(rightHandSide) : new QrDecomposition(leftHandSide).solve(rightHandSide); + } + + Matrix.prototype.solve = function (other) { + return solve(this, other); + }; + + module.exports = { + SingularValueDecomposition: SingularValueDecomposition, + SVD: SingularValueDecomposition, + EigenvalueDecomposition: EigenvalueDecomposition, + EVD: EigenvalueDecomposition, + LuDecomposition: LuDecomposition, + LU: LuDecomposition, + QrDecomposition: QrDecomposition, + QR: QrDecomposition, + CholeskyDecomposition: CholeskyDecomposition, + CHO: CholeskyDecomposition, + inverse: inverse, + solve: solve + }; + + +/***/ }, +/* 116 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var Matrix = __webpack_require__(114); + var hypotenuse = __webpack_require__(117).hypotenuse; + + // https://github.com/lutzroeder/Mapack/blob/master/Source/SingularValueDecomposition.cs + function SingularValueDecomposition(value, options) { + if (!(this instanceof SingularValueDecomposition)) { + return new SingularValueDecomposition(value, options); + } + value = Matrix.checkMatrix(value); + + options = options || {}; + + var a = value.clone(), + m = value.rows, + n = value.columns, + nu = Math.min(m, n); + + var wantu = true, wantv = true; + if (options.computeLeftSingularVectors === false) + wantu = false; + if (options.computeRightSingularVectors === false) + wantv = false; + var autoTranspose = options.autoTranspose === true; + + var swapped = false; + if (m < n) { + if (!autoTranspose) { + console.warn('Computing SVD on a matrix with more columns than rows. Consider enabling autoTranspose'); + } else { + a = a.transpose(); + m = a.rows; + n = a.columns; + swapped = true; + var aux = wantu; + wantu = wantv; + wantv = aux; + } + } + + var s = new Array(Math.min(m + 1, n)), + U = Matrix.zeros(m, nu), + V = Matrix.zeros(n, n), + e = new Array(n), + work = new Array(m); + + var nct = Math.min(m - 1, n); + var nrt = Math.max(0, Math.min(n - 2, m)); + + var i, j, k, p, t, ks, f, cs, sn, max, kase, + scale, sp, spm1, epm1, sk, ek, b, c, shift, g; + + for (k = 0, max = Math.max(nct, nrt); k < max; k++) { + if (k < nct) { + s[k] = 0; + for (i = k; i < m; i++) { + s[k] = hypotenuse(s[k], a[i][k]); + } + if (s[k] !== 0) { + if (a[k][k] < 0) { + s[k] = -s[k]; + } + for (i = k; i < m; i++) { + a[i][k] /= s[k]; + } + a[k][k] += 1; + } + s[k] = -s[k]; + } + + for (j = k + 1; j < n; j++) { + if ((k < nct) && (s[k] !== 0)) { + t = 0; + for (i = k; i < m; i++) { + t += a[i][k] * a[i][j]; + } + t = -t / a[k][k]; + for (i = k; i < m; i++) { + a[i][j] += t * a[i][k]; + } + } + e[j] = a[k][j]; + } + + if (wantu && (k < nct)) { + for (i = k; i < m; i++) { + U[i][k] = a[i][k]; + } + } + + if (k < nrt) { + e[k] = 0; + for (i = k + 1; i < n; i++) { + e[k] = hypotenuse(e[k], e[i]); + } + if (e[k] !== 0) { + if (e[k + 1] < 0) + e[k] = -e[k]; + for (i = k + 1; i < n; i++) { + e[i] /= e[k]; + } + e[k + 1] += 1; + } + e[k] = -e[k]; + if ((k + 1 < m) && (e[k] !== 0)) { + for (i = k + 1; i < m; i++) { + work[i] = 0; + } + for (j = k + 1; j < n; j++) { + for (i = k + 1; i < m; i++) { + work[i] += e[j] * a[i][j]; + } + } + for (j = k + 1; j < n; j++) { + t = -e[j] / e[k + 1]; + for (i = k + 1; i < m; i++) { + a[i][j] += t * work[i]; + } + } + } + if (wantv) { + for (i = k + 1; i < n; i++) { + V[i][k] = e[i]; + } + } + } + } + + p = Math.min(n, m + 1); + if (nct < n) { + s[nct] = a[nct][nct]; + } + if (m < p) { + s[p - 1] = 0; + } + if (nrt + 1 < p) { + e[nrt] = a[nrt][p - 1]; + } + e[p - 1] = 0; + + if (wantu) { + for (j = nct; j < nu; j++) { + for (i = 0; i < m; i++) { + U[i][j] = 0; + } + U[j][j] = 1; + } + for (k = nct - 1; k >= 0; k--) { + if (s[k] !== 0) { + for (j = k + 1; j < nu; j++) { + t = 0; + for (i = k; i < m; i++) { + t += U[i][k] * U[i][j]; + } + t = -t / U[k][k]; + for (i = k; i < m; i++) { + U[i][j] += t * U[i][k]; + } + } + for (i = k; i < m; i++) { + U[i][k] = -U[i][k]; + } + U[k][k] = 1 + U[k][k]; + for (i = 0; i < k - 1; i++) { + U[i][k] = 0; + } + } else { + for (i = 0; i < m; i++) { + U[i][k] = 0; + } + U[k][k] = 1; + } + } + } + + if (wantv) { + for (k = n - 1; k >= 0; k--) { + if ((k < nrt) && (e[k] !== 0)) { + for (j = k + 1; j < n; j++) { + t = 0; + for (i = k + 1; i < n; i++) { + t += V[i][k] * V[i][j]; + } + t = -t / V[k + 1][k]; + for (i = k + 1; i < n; i++) { + V[i][j] += t * V[i][k]; + } + } + } + for (i = 0; i < n; i++) { + V[i][k] = 0; + } + V[k][k] = 1; + } + } + + var pp = p - 1, + iter = 0, + eps = Math.pow(2, -52); + while (p > 0) { + for (k = p - 2; k >= -1; k--) { + if (k === -1) { + break; + } + if (Math.abs(e[k]) <= eps * (Math.abs(s[k]) + Math.abs(s[k + 1]))) { + e[k] = 0; + break; + } + } + if (k === p - 2) { + kase = 4; + } else { + for (ks = p - 1; ks >= k; ks--) { + if (ks === k) { + break; + } + t = (ks !== p ? Math.abs(e[ks]) : 0) + (ks !== k + 1 ? Math.abs(e[ks - 1]) : 0); + if (Math.abs(s[ks]) <= eps * t) { + s[ks] = 0; + break; + } + } + if (ks === k) { + kase = 3; + } else if (ks === p - 1) { + kase = 1; + } else { + kase = 2; + k = ks; + } + } + + k++; + + switch (kase) { + case 1: { + f = e[p - 2]; + e[p - 2] = 0; + for (j = p - 2; j >= k; j--) { + t = hypotenuse(s[j], f); + cs = s[j] / t; + sn = f / t; + s[j] = t; + if (j !== k) { + f = -sn * e[j - 1]; + e[j - 1] = cs * e[j - 1]; + } + if (wantv) { + for (i = 0; i < n; i++) { + t = cs * V[i][j] + sn * V[i][p - 1]; + V[i][p - 1] = -sn * V[i][j] + cs * V[i][p - 1]; + V[i][j] = t; + } + } + } + break; + } + case 2 : { + f = e[k - 1]; + e[k - 1] = 0; + for (j = k; j < p; j++) { + t = hypotenuse(s[j], f); + cs = s[j] / t; + sn = f / t; + s[j] = t; + f = -sn * e[j]; + e[j] = cs * e[j]; + if (wantu) { + for (i = 0; i < m; i++) { + t = cs * U[i][j] + sn * U[i][k - 1]; + U[i][k - 1] = -sn * U[i][j] + cs * U[i][k - 1]; + U[i][j] = t; + } + } + } + break; + } + case 3 : { + scale = Math.max(Math.max(Math.max(Math.max(Math.abs(s[p - 1]), Math.abs(s[p - 2])), Math.abs(e[p - 2])), Math.abs(s[k])), Math.abs(e[k])); + sp = s[p - 1] / scale; + spm1 = s[p - 2] / scale; + epm1 = e[p - 2] / scale; + sk = s[k] / scale; + ek = e[k] / scale; + b = ((spm1 + sp) * (spm1 - sp) + epm1 * epm1) / 2; + c = (sp * epm1) * (sp * epm1); + shift = 0; + if ((b !== 0) || (c !== 0)) { + shift = Math.sqrt(b * b + c); + if (b < 0) { + shift = -shift; + } + shift = c / (b + shift); + } + f = (sk + sp) * (sk - sp) + shift; + g = sk * ek; + for (j = k; j < p - 1; j++) { + t = hypotenuse(f, g); + cs = f / t; + sn = g / t; + if (j !== k) { + e[j - 1] = t; + } + f = cs * s[j] + sn * e[j]; + e[j] = cs * e[j] - sn * s[j]; + g = sn * s[j + 1]; + s[j + 1] = cs * s[j + 1]; + if (wantv) { + for (i = 0; i < n; i++) { + t = cs * V[i][j] + sn * V[i][j + 1]; + V[i][j + 1] = -sn * V[i][j] + cs * V[i][j + 1]; + V[i][j] = t; + } + } + t = hypotenuse(f, g); + cs = f / t; + sn = g / t; + s[j] = t; + f = cs * e[j] + sn * s[j + 1]; + s[j + 1] = -sn * e[j] + cs * s[j + 1]; + g = sn * e[j + 1]; + e[j + 1] = cs * e[j + 1]; + if (wantu && (j < m - 1)) { + for (i = 0; i < m; i++) { + t = cs * U[i][j] + sn * U[i][j + 1]; + U[i][j + 1] = -sn * U[i][j] + cs * U[i][j + 1]; + U[i][j] = t; + } + } + } + e[p - 2] = f; + iter = iter + 1; + break; + } + case 4: { + if (s[k] <= 0) { + s[k] = (s[k] < 0 ? -s[k] : 0); + if (wantv) { + for (i = 0; i <= pp; i++) { + V[i][k] = -V[i][k]; + } + } + } + while (k < pp) { + if (s[k] >= s[k + 1]) { + break; + } + t = s[k]; + s[k] = s[k + 1]; + s[k + 1] = t; + if (wantv && (k < n - 1)) { + for (i = 0; i < n; i++) { + t = V[i][k + 1]; + V[i][k + 1] = V[i][k]; + V[i][k] = t; + } + } + if (wantu && (k < m - 1)) { + for (i = 0; i < m; i++) { + t = U[i][k + 1]; + U[i][k + 1] = U[i][k]; + U[i][k] = t; + } + } + k++; + } + iter = 0; + p--; + break; + } + } + } + + if (swapped) { + var tmp = V; + V = U; + U = tmp; + } + + this.m = m; + this.n = n; + this.s = s; + this.U = U; + this.V = V; + } + + SingularValueDecomposition.prototype = { + get condition() { + return this.s[0] / this.s[Math.min(this.m, this.n) - 1]; + }, + get norm2() { + return this.s[0]; + }, + get rank() { + var eps = Math.pow(2, -52), + tol = Math.max(this.m, this.n) * this.s[0] * eps, + r = 0, + s = this.s; + for (var i = 0, ii = s.length; i < ii; i++) { + if (s[i] > tol) { + r++; + } + } + return r; + }, + get diagonal() { + return this.s; + }, + // https://github.com/accord-net/framework/blob/development/Sources/Accord.Math/Decompositions/SingularValueDecomposition.cs + get threshold() { + return (Math.pow(2, -52) / 2) * Math.max(this.m, this.n) * this.s[0]; + }, + get leftSingularVectors() { + return this.U; + }, + get rightSingularVectors() { + return this.V; + }, + get diagonalMatrix() { + return Matrix.diag(this.s); + }, + solve: function (value) { + + var Y = value, + e = this.threshold, + scols = this.s.length, + Ls = Matrix.zeros(scols, scols), + i; + + for (i = 0; i < scols; i++) { + if (Math.abs(this.s[i]) <= e) { + Ls[i][i] = 0; + } else { + Ls[i][i] = 1 / this.s[i]; + } + } + + + var VL = this.V.mmul(Ls), + vrows = this.V.rows, + urows = this.U.rows, + VLU = Matrix.zeros(vrows, urows), + j, k, sum; + + for (i = 0; i < vrows; i++) { + for (j = 0; j < urows; j++) { + sum = 0; + for (k = 0; k < scols; k++) { + sum += VL[i][k] * this.U[j][k]; + } + VLU[i][j] = sum; + } + } + + return VLU.mmul(Y); + }, + solveForDiagonal: function (value) { + return this.solve(Matrix.diag(value)); + }, + inverse: function () { + var e = this.threshold, + vrows = this.V.rows, + vcols = this.V.columns, + X = new Matrix(vrows, this.s.length), + i, j; + + for (i = 0; i < vrows; i++) { + for (j = 0; j < vcols; j++) { + if (Math.abs(this.s[j]) > e) { + X[i][j] = this.V[i][j] / this.s[j]; + } else { + X[i][j] = 0; + } + } + } + + var urows = this.U.rows, + ucols = this.U.columns, + Y = new Matrix(vrows, urows), + k, sum; + + for (i = 0; i < vrows; i++) { + for (j = 0; j < urows; j++) { + sum = 0; + for (k = 0; k < ucols; k++) { + sum += X[i][k] * this.U[j][k]; + } + Y[i][j] = sum; + } + } + + return Y; + } + }; + + module.exports = SingularValueDecomposition; + + +/***/ }, +/* 117 */ +/***/ function(module, exports) { + + 'use strict'; + + exports.hypotenuse = function hypotenuse(a, b) { + var r; + if (Math.abs(a) > Math.abs(b)) { + r = b / a; + return Math.abs(a) * Math.sqrt(1 + r * r); + } + if (b !== 0) { + r = a / b; + return Math.abs(b) * Math.sqrt(1 + r * r); + } + return 0; + }; + + +/***/ }, +/* 118 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var Matrix = __webpack_require__(114); + var hypotenuse = __webpack_require__(117).hypotenuse; + + // https://github.com/lutzroeder/Mapack/blob/master/Source/EigenvalueDecomposition.cs + function EigenvalueDecomposition(matrix) { + if (!(this instanceof EigenvalueDecomposition)) { + return new EigenvalueDecomposition(matrix); + } + matrix = Matrix.checkMatrix(matrix); + if (!matrix.isSquare()) { + throw new Error('Matrix is not a square matrix'); + } + + var n = matrix.columns, + V = Matrix.zeros(n, n), + d = new Array(n), + e = new Array(n), + value = matrix, + i, j; + + if (matrix.isSymmetric()) { + for (i = 0; i < n; i++) { + for (j = 0; j < n; j++) { + V[i][j] = value[i][j]; + } + } + tred2(n, e, d, V); + tql2(n, e, d, V); + } + else { + var H = Matrix.zeros(n, n), + ort = new Array(n); + for (j = 0; j < n; j++) { + for (i = 0; i < n; i++) { + H[i][j] = value[i][j]; + } + } + orthes(n, H, ort, V); + hqr2(n, e, d, V, H); + } + + this.n = n; + this.e = e; + this.d = d; + this.V = V; + } + + EigenvalueDecomposition.prototype = { + get realEigenvalues() { + return this.d; + }, + get imaginaryEigenvalues() { + return this.e; + }, + get eigenvectorMatrix() { + return this.V; + }, + get diagonalMatrix() { + var n = this.n, + e = this.e, + d = this.d, + X = new Matrix(n, n), + i, j; + for (i = 0; i < n; i++) { + for (j = 0; j < n; j++) { + X[i][j] = 0; + } + X[i][i] = d[i]; + if (e[i] > 0) { + X[i][i + 1] = e[i]; + } + else if (e[i] < 0) { + X[i][i - 1] = e[i]; + } + } + return X; + } + }; + + function tred2(n, e, d, V) { + + var f, g, h, i, j, k, + hh, scale; + + for (j = 0; j < n; j++) { + d[j] = V[n - 1][j]; + } + + for (i = n - 1; i > 0; i--) { + scale = 0; + h = 0; + for (k = 0; k < i; k++) { + scale = scale + Math.abs(d[k]); + } + + if (scale === 0) { + e[i] = d[i - 1]; + for (j = 0; j < i; j++) { + d[j] = V[i - 1][j]; + V[i][j] = 0; + V[j][i] = 0; + } + } else { + for (k = 0; k < i; k++) { + d[k] /= scale; + h += d[k] * d[k]; + } + + f = d[i - 1]; + g = Math.sqrt(h); + if (f > 0) { + g = -g; + } + + e[i] = scale * g; + h = h - f * g; + d[i - 1] = f - g; + for (j = 0; j < i; j++) { + e[j] = 0; + } + + for (j = 0; j < i; j++) { + f = d[j]; + V[j][i] = f; + g = e[j] + V[j][j] * f; + for (k = j + 1; k <= i - 1; k++) { + g += V[k][j] * d[k]; + e[k] += V[k][j] * f; + } + e[j] = g; + } + + f = 0; + for (j = 0; j < i; j++) { + e[j] /= h; + f += e[j] * d[j]; + } + + hh = f / (h + h); + for (j = 0; j < i; j++) { + e[j] -= hh * d[j]; + } + + for (j = 0; j < i; j++) { + f = d[j]; + g = e[j]; + for (k = j; k <= i - 1; k++) { + V[k][j] -= (f * e[k] + g * d[k]); + } + d[j] = V[i - 1][j]; + V[i][j] = 0; + } + } + d[i] = h; + } + + for (i = 0; i < n - 1; i++) { + V[n - 1][i] = V[i][i]; + V[i][i] = 1; + h = d[i + 1]; + if (h !== 0) { + for (k = 0; k <= i; k++) { + d[k] = V[k][i + 1] / h; + } + + for (j = 0; j <= i; j++) { + g = 0; + for (k = 0; k <= i; k++) { + g += V[k][i + 1] * V[k][j]; + } + for (k = 0; k <= i; k++) { + V[k][j] -= g * d[k]; + } + } + } + + for (k = 0; k <= i; k++) { + V[k][i + 1] = 0; + } + } + + for (j = 0; j < n; j++) { + d[j] = V[n - 1][j]; + V[n - 1][j] = 0; + } + + V[n - 1][n - 1] = 1; + e[0] = 0; + } + + function tql2(n, e, d, V) { + + var g, h, i, j, k, l, m, p, r, + dl1, c, c2, c3, el1, s, s2, + iter; + + for (i = 1; i < n; i++) { + e[i - 1] = e[i]; + } + + e[n - 1] = 0; + + var f = 0, + tst1 = 0, + eps = Math.pow(2, -52); + + for (l = 0; l < n; l++) { + tst1 = Math.max(tst1, Math.abs(d[l]) + Math.abs(e[l])); + m = l; + while (m < n) { + if (Math.abs(e[m]) <= eps * tst1) { + break; + } + m++; + } + + if (m > l) { + iter = 0; + do { + iter = iter + 1; + + g = d[l]; + p = (d[l + 1] - g) / (2 * e[l]); + r = hypotenuse(p, 1); + if (p < 0) { + r = -r; + } + + d[l] = e[l] / (p + r); + d[l + 1] = e[l] * (p + r); + dl1 = d[l + 1]; + h = g - d[l]; + for (i = l + 2; i < n; i++) { + d[i] -= h; + } + + f = f + h; + + p = d[m]; + c = 1; + c2 = c; + c3 = c; + el1 = e[l + 1]; + s = 0; + s2 = 0; + for (i = m - 1; i >= l; i--) { + c3 = c2; + c2 = c; + s2 = s; + g = c * e[i]; + h = c * p; + r = hypotenuse(p, e[i]); + e[i + 1] = s * r; + s = e[i] / r; + c = p / r; + p = c * d[i] - s * g; + d[i + 1] = h + s * (c * g + s * d[i]); + + for (k = 0; k < n; k++) { + h = V[k][i + 1]; + V[k][i + 1] = s * V[k][i] + c * h; + V[k][i] = c * V[k][i] - s * h; + } + } + + p = -s * s2 * c3 * el1 * e[l] / dl1; + e[l] = s * p; + d[l] = c * p; + + } + while (Math.abs(e[l]) > eps * tst1); + } + d[l] = d[l] + f; + e[l] = 0; + } + + for (i = 0; i < n - 1; i++) { + k = i; + p = d[i]; + for (j = i + 1; j < n; j++) { + if (d[j] < p) { + k = j; + p = d[j]; + } + } + + if (k !== i) { + d[k] = d[i]; + d[i] = p; + for (j = 0; j < n; j++) { + p = V[j][i]; + V[j][i] = V[j][k]; + V[j][k] = p; + } + } + } + } + + function orthes(n, H, ort, V) { + + var low = 0, + high = n - 1, + f, g, h, i, j, m, + scale; + + for (m = low + 1; m <= high - 1; m++) { + scale = 0; + for (i = m; i <= high; i++) { + scale = scale + Math.abs(H[i][m - 1]); + } + + if (scale !== 0) { + h = 0; + for (i = high; i >= m; i--) { + ort[i] = H[i][m - 1] / scale; + h += ort[i] * ort[i]; + } + + g = Math.sqrt(h); + if (ort[m] > 0) { + g = -g; + } + + h = h - ort[m] * g; + ort[m] = ort[m] - g; + + for (j = m; j < n; j++) { + f = 0; + for (i = high; i >= m; i--) { + f += ort[i] * H[i][j]; + } + + f = f / h; + for (i = m; i <= high; i++) { + H[i][j] -= f * ort[i]; + } + } + + for (i = 0; i <= high; i++) { + f = 0; + for (j = high; j >= m; j--) { + f += ort[j] * H[i][j]; + } + + f = f / h; + for (j = m; j <= high; j++) { + H[i][j] -= f * ort[j]; + } + } + + ort[m] = scale * ort[m]; + H[m][m - 1] = scale * g; + } + } + + for (i = 0; i < n; i++) { + for (j = 0; j < n; j++) { + V[i][j] = (i === j ? 1 : 0); + } + } + + for (m = high - 1; m >= low + 1; m--) { + if (H[m][m - 1] !== 0) { + for (i = m + 1; i <= high; i++) { + ort[i] = H[i][m - 1]; + } + + for (j = m; j <= high; j++) { + g = 0; + for (i = m; i <= high; i++) { + g += ort[i] * V[i][j]; + } + + g = (g / ort[m]) / H[m][m - 1]; + for (i = m; i <= high; i++) { + V[i][j] += g * ort[i]; + } + } + } + } + } + + function hqr2(nn, e, d, V, H) { + var n = nn - 1, + low = 0, + high = nn - 1, + eps = Math.pow(2, -52), + exshift = 0, + norm = 0, + p = 0, + q = 0, + r = 0, + s = 0, + z = 0, + iter = 0, + i, j, k, l, m, t, w, x, y, + ra, sa, vr, vi, + notlast, cdivres; + + for (i = 0; i < nn; i++) { + if (i < low || i > high) { + d[i] = H[i][i]; + e[i] = 0; + } + + for (j = Math.max(i - 1, 0); j < nn; j++) { + norm = norm + Math.abs(H[i][j]); + } + } + + while (n >= low) { + l = n; + while (l > low) { + s = Math.abs(H[l - 1][l - 1]) + Math.abs(H[l][l]); + if (s === 0) { + s = norm; + } + if (Math.abs(H[l][l - 1]) < eps * s) { + break; + } + l--; + } + + if (l === n) { + H[n][n] = H[n][n] + exshift; + d[n] = H[n][n]; + e[n] = 0; + n--; + iter = 0; + } else if (l === n - 1) { + w = H[n][n - 1] * H[n - 1][n]; + p = (H[n - 1][n - 1] - H[n][n]) / 2; + q = p * p + w; + z = Math.sqrt(Math.abs(q)); + H[n][n] = H[n][n] + exshift; + H[n - 1][n - 1] = H[n - 1][n - 1] + exshift; + x = H[n][n]; + + if (q >= 0) { + z = (p >= 0) ? (p + z) : (p - z); + d[n - 1] = x + z; + d[n] = d[n - 1]; + if (z !== 0) { + d[n] = x - w / z; + } + e[n - 1] = 0; + e[n] = 0; + x = H[n][n - 1]; + s = Math.abs(x) + Math.abs(z); + p = x / s; + q = z / s; + r = Math.sqrt(p * p + q * q); + p = p / r; + q = q / r; + + for (j = n - 1; j < nn; j++) { + z = H[n - 1][j]; + H[n - 1][j] = q * z + p * H[n][j]; + H[n][j] = q * H[n][j] - p * z; + } + + for (i = 0; i <= n; i++) { + z = H[i][n - 1]; + H[i][n - 1] = q * z + p * H[i][n]; + H[i][n] = q * H[i][n] - p * z; + } + + for (i = low; i <= high; i++) { + z = V[i][n - 1]; + V[i][n - 1] = q * z + p * V[i][n]; + V[i][n] = q * V[i][n] - p * z; + } + } else { + d[n - 1] = x + p; + d[n] = x + p; + e[n - 1] = z; + e[n] = -z; + } + + n = n - 2; + iter = 0; + } else { + x = H[n][n]; + y = 0; + w = 0; + if (l < n) { + y = H[n - 1][n - 1]; + w = H[n][n - 1] * H[n - 1][n]; + } + + if (iter === 10) { + exshift += x; + for (i = low; i <= n; i++) { + H[i][i] -= x; + } + s = Math.abs(H[n][n - 1]) + Math.abs(H[n - 1][n - 2]); + x = y = 0.75 * s; + w = -0.4375 * s * s; + } + + if (iter === 30) { + s = (y - x) / 2; + s = s * s + w; + if (s > 0) { + s = Math.sqrt(s); + if (y < x) { + s = -s; + } + s = x - w / ((y - x) / 2 + s); + for (i = low; i <= n; i++) { + H[i][i] -= s; + } + exshift += s; + x = y = w = 0.964; + } + } + + iter = iter + 1; + + m = n - 2; + while (m >= l) { + z = H[m][m]; + r = x - z; + s = y - z; + p = (r * s - w) / H[m + 1][m] + H[m][m + 1]; + q = H[m + 1][m + 1] - z - r - s; + r = H[m + 2][m + 1]; + s = Math.abs(p) + Math.abs(q) + Math.abs(r); + p = p / s; + q = q / s; + r = r / s; + if (m === l) { + break; + } + if (Math.abs(H[m][m - 1]) * (Math.abs(q) + Math.abs(r)) < eps * (Math.abs(p) * (Math.abs(H[m - 1][m - 1]) + Math.abs(z) + Math.abs(H[m + 1][m + 1])))) { + break; + } + m--; + } + + for (i = m + 2; i <= n; i++) { + H[i][i - 2] = 0; + if (i > m + 2) { + H[i][i - 3] = 0; + } + } + + for (k = m; k <= n - 1; k++) { + notlast = (k !== n - 1); + if (k !== m) { + p = H[k][k - 1]; + q = H[k + 1][k - 1]; + r = (notlast ? H[k + 2][k - 1] : 0); + x = Math.abs(p) + Math.abs(q) + Math.abs(r); + if (x !== 0) { + p = p / x; + q = q / x; + r = r / x; + } + } + + if (x === 0) { + break; + } + + s = Math.sqrt(p * p + q * q + r * r); + if (p < 0) { + s = -s; + } + + if (s !== 0) { + if (k !== m) { + H[k][k - 1] = -s * x; + } else if (l !== m) { + H[k][k - 1] = -H[k][k - 1]; + } + + p = p + s; + x = p / s; + y = q / s; + z = r / s; + q = q / p; + r = r / p; + + for (j = k; j < nn; j++) { + p = H[k][j] + q * H[k + 1][j]; + if (notlast) { + p = p + r * H[k + 2][j]; + H[k + 2][j] = H[k + 2][j] - p * z; + } + + H[k][j] = H[k][j] - p * x; + H[k + 1][j] = H[k + 1][j] - p * y; + } + + for (i = 0; i <= Math.min(n, k + 3); i++) { + p = x * H[i][k] + y * H[i][k + 1]; + if (notlast) { + p = p + z * H[i][k + 2]; + H[i][k + 2] = H[i][k + 2] - p * r; + } + + H[i][k] = H[i][k] - p; + H[i][k + 1] = H[i][k + 1] - p * q; + } + + for (i = low; i <= high; i++) { + p = x * V[i][k] + y * V[i][k + 1]; + if (notlast) { + p = p + z * V[i][k + 2]; + V[i][k + 2] = V[i][k + 2] - p * r; + } + + V[i][k] = V[i][k] - p; + V[i][k + 1] = V[i][k + 1] - p * q; + } + } + } + } + } + + if (norm === 0) { + return; + } + + for (n = nn - 1; n >= 0; n--) { + p = d[n]; + q = e[n]; + + if (q === 0) { + l = n; + H[n][n] = 1; + for (i = n - 1; i >= 0; i--) { + w = H[i][i] - p; + r = 0; + for (j = l; j <= n; j++) { + r = r + H[i][j] * H[j][n]; + } + + if (e[i] < 0) { + z = w; + s = r; + } else { + l = i; + if (e[i] === 0) { + H[i][n] = (w !== 0) ? (-r / w) : (-r / (eps * norm)); + } else { + x = H[i][i + 1]; + y = H[i + 1][i]; + q = (d[i] - p) * (d[i] - p) + e[i] * e[i]; + t = (x * s - z * r) / q; + H[i][n] = t; + H[i + 1][n] = (Math.abs(x) > Math.abs(z)) ? ((-r - w * t) / x) : ((-s - y * t) / z); + } + + t = Math.abs(H[i][n]); + if ((eps * t) * t > 1) { + for (j = i; j <= n; j++) { + H[j][n] = H[j][n] / t; + } + } + } + } + } else if (q < 0) { + l = n - 1; + + if (Math.abs(H[n][n - 1]) > Math.abs(H[n - 1][n])) { + H[n - 1][n - 1] = q / H[n][n - 1]; + H[n - 1][n] = -(H[n][n] - p) / H[n][n - 1]; + } else { + cdivres = cdiv(0, -H[n - 1][n], H[n - 1][n - 1] - p, q); + H[n - 1][n - 1] = cdivres[0]; + H[n - 1][n] = cdivres[1]; + } + + H[n][n - 1] = 0; + H[n][n] = 1; + for (i = n - 2; i >= 0; i--) { + ra = 0; + sa = 0; + for (j = l; j <= n; j++) { + ra = ra + H[i][j] * H[j][n - 1]; + sa = sa + H[i][j] * H[j][n]; + } + + w = H[i][i] - p; + + if (e[i] < 0) { + z = w; + r = ra; + s = sa; + } else { + l = i; + if (e[i] === 0) { + cdivres = cdiv(-ra, -sa, w, q); + H[i][n - 1] = cdivres[0]; + H[i][n] = cdivres[1]; + } else { + x = H[i][i + 1]; + y = H[i + 1][i]; + vr = (d[i] - p) * (d[i] - p) + e[i] * e[i] - q * q; + vi = (d[i] - p) * 2 * q; + if (vr === 0 && vi === 0) { + vr = eps * norm * (Math.abs(w) + Math.abs(q) + Math.abs(x) + Math.abs(y) + Math.abs(z)); + } + cdivres = cdiv(x * r - z * ra + q * sa, x * s - z * sa - q * ra, vr, vi); + H[i][n - 1] = cdivres[0]; + H[i][n] = cdivres[1]; + if (Math.abs(x) > (Math.abs(z) + Math.abs(q))) { + H[i + 1][n - 1] = (-ra - w * H[i][n - 1] + q * H[i][n]) / x; + H[i + 1][n] = (-sa - w * H[i][n] - q * H[i][n - 1]) / x; + } else { + cdivres = cdiv(-r - y * H[i][n - 1], -s - y * H[i][n], z, q); + H[i + 1][n - 1] = cdivres[0]; + H[i + 1][n] = cdivres[1]; + } + } + + t = Math.max(Math.abs(H[i][n - 1]), Math.abs(H[i][n])); + if ((eps * t) * t > 1) { + for (j = i; j <= n; j++) { + H[j][n - 1] = H[j][n - 1] / t; + H[j][n] = H[j][n] / t; + } + } + } + } + } + } + + for (i = 0; i < nn; i++) { + if (i < low || i > high) { + for (j = i; j < nn; j++) { + V[i][j] = H[i][j]; + } + } + } + + for (j = nn - 1; j >= low; j--) { + for (i = low; i <= high; i++) { + z = 0; + for (k = low; k <= Math.min(j, high); k++) { + z = z + V[i][k] * H[k][j]; + } + V[i][j] = z; + } + } + } + + function cdiv(xr, xi, yr, yi) { + var r, d; + if (Math.abs(yr) > Math.abs(yi)) { + r = yi / yr; + d = yr + r * yi; + return [(xr + r * xi) / d, (xi - r * xr) / d]; + } + else { + r = yr / yi; + d = yi + r * yr; + return [(r * xr + xi) / d, (r * xi - xr) / d]; + } + } + + module.exports = EigenvalueDecomposition; + + +/***/ }, +/* 119 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var Matrix = __webpack_require__(114); + + // https://github.com/lutzroeder/Mapack/blob/master/Source/LuDecomposition.cs + function LuDecomposition(matrix) { + if (!(this instanceof LuDecomposition)) { + return new LuDecomposition(matrix); + } + matrix = Matrix.checkMatrix(matrix); + + var lu = matrix.clone(), + rows = lu.rows, + columns = lu.columns, + pivotVector = new Array(rows), + pivotSign = 1, + i, j, k, p, s, t, v, + LUrowi, LUcolj, kmax; + + for (i = 0; i < rows; i++) { + pivotVector[i] = i; + } + + LUcolj = new Array(rows); + + for (j = 0; j < columns; j++) { + + for (i = 0; i < rows; i++) { + LUcolj[i] = lu[i][j]; + } + + for (i = 0; i < rows; i++) { + LUrowi = lu[i]; + kmax = Math.min(i, j); + s = 0; + for (k = 0; k < kmax; k++) { + s += LUrowi[k] * LUcolj[k]; + } + LUrowi[j] = LUcolj[i] -= s; + } + + p = j; + for (i = j + 1; i < rows; i++) { + if (Math.abs(LUcolj[i]) > Math.abs(LUcolj[p])) { + p = i; + } + } + + if (p !== j) { + for (k = 0; k < columns; k++) { + t = lu[p][k]; + lu[p][k] = lu[j][k]; + lu[j][k] = t; + } + + v = pivotVector[p]; + pivotVector[p] = pivotVector[j]; + pivotVector[j] = v; + + pivotSign = -pivotSign; + } + + if (j < rows && lu[j][j] !== 0) { + for (i = j + 1; i < rows; i++) { + lu[i][j] /= lu[j][j]; + } + } + } + + this.LU = lu; + this.pivotVector = pivotVector; + this.pivotSign = pivotSign; + } + + LuDecomposition.prototype = { + isSingular: function () { + var data = this.LU, + col = data.columns; + for (var j = 0; j < col; j++) { + if (data[j][j] === 0) { + return true; + } + } + return false; + }, + get determinant() { + var data = this.LU; + if (!data.isSquare()) + throw new Error('Matrix must be square'); + var determinant = this.pivotSign, col = data.columns; + for (var j = 0; j < col; j++) + determinant *= data[j][j]; + return determinant; + }, + get lowerTriangularFactor() { + var data = this.LU, + rows = data.rows, + columns = data.columns, + X = new Matrix(rows, columns); + for (var i = 0; i < rows; i++) { + for (var j = 0; j < columns; j++) { + if (i > j) { + X[i][j] = data[i][j]; + } else if (i === j) { + X[i][j] = 1; + } else { + X[i][j] = 0; + } + } + } + return X; + }, + get upperTriangularFactor() { + var data = this.LU, + rows = data.rows, + columns = data.columns, + X = new Matrix(rows, columns); + for (var i = 0; i < rows; i++) { + for (var j = 0; j < columns; j++) { + if (i <= j) { + X[i][j] = data[i][j]; + } else { + X[i][j] = 0; + } + } + } + return X; + }, + get pivotPermutationVector() { + return this.pivotVector.slice(); + }, + solve: function (value) { + value = Matrix.checkMatrix(value); + + var lu = this.LU, + rows = lu.rows; + + if (rows !== value.rows) + throw new Error('Invalid matrix dimensions'); + if (this.isSingular()) + throw new Error('LU matrix is singular'); + + var count = value.columns, + X = value.subMatrixRow(this.pivotVector, 0, count - 1), + columns = lu.columns, + i, j, k; + + for (k = 0; k < columns; k++) { + for (i = k + 1; i < columns; i++) { + for (j = 0; j < count; j++) { + X[i][j] -= X[k][j] * lu[i][k]; + } + } + } + for (k = columns - 1; k >= 0; k--) { + for (j = 0; j < count; j++) { + X[k][j] /= lu[k][k]; + } + for (i = 0; i < k; i++) { + for (j = 0; j < count; j++) { + X[i][j] -= X[k][j] * lu[i][k]; + } + } + } + return X; + } + }; + + module.exports = LuDecomposition; + + +/***/ }, +/* 120 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var Matrix = __webpack_require__(114); + var hypotenuse = __webpack_require__(117).hypotenuse; + + //https://github.com/lutzroeder/Mapack/blob/master/Source/QrDecomposition.cs + function QrDecomposition(value) { + if (!(this instanceof QrDecomposition)) { + return new QrDecomposition(value); + } + value = Matrix.checkMatrix(value); + + var qr = value.clone(), + m = value.rows, + n = value.columns, + rdiag = new Array(n), + i, j, k, s; + + for (k = 0; k < n; k++) { + var nrm = 0; + for (i = k; i < m; i++) { + nrm = hypotenuse(nrm, qr[i][k]); + } + if (nrm !== 0) { + if (qr[k][k] < 0) { + nrm = -nrm; + } + for (i = k; i < m; i++) { + qr[i][k] /= nrm; + } + qr[k][k] += 1; + for (j = k + 1; j < n; j++) { + s = 0; + for (i = k; i < m; i++) { + s += qr[i][k] * qr[i][j]; + } + s = -s / qr[k][k]; + for (i = k; i < m; i++) { + qr[i][j] += s * qr[i][k]; + } + } + } + rdiag[k] = -nrm; + } + + this.QR = qr; + this.Rdiag = rdiag; + } + + QrDecomposition.prototype = { + solve: function (value) { + value = Matrix.checkMatrix(value); + + var qr = this.QR, + m = qr.rows; + + if (value.rows !== m) + throw new Error('Matrix row dimensions must agree'); + if (!this.isFullRank()) + throw new Error('Matrix is rank deficient'); + + var count = value.columns, + X = value.clone(), + n = qr.columns, + i, j, k, s; + + for (k = 0; k < n; k++) { + for (j = 0; j < count; j++) { + s = 0; + for (i = k; i < m; i++) { + s += qr[i][k] * X[i][j]; + } + s = -s / qr[k][k]; + for (i = k; i < m; i++) { + X[i][j] += s * qr[i][k]; + } + } + } + for (k = n - 1; k >= 0; k--) { + for (j = 0; j < count; j++) { + X[k][j] /= this.Rdiag[k]; + } + for (i = 0; i < k; i++) { + for (j = 0; j < count; j++) { + X[i][j] -= X[k][j] * qr[i][k]; + } + } + } + + return X.subMatrix(0, n - 1, 0, count - 1); + }, + isFullRank: function () { + var columns = this.QR.columns; + for (var i = 0; i < columns; i++) { + if (this.Rdiag[i] === 0) { + return false; + } + } + return true; + }, + get upperTriangularFactor() { + var qr = this.QR, + n = qr.columns, + X = new Matrix(n, n), + i, j; + for (i = 0; i < n; i++) { + for (j = 0; j < n; j++) { + if (i < j) { + X[i][j] = qr[i][j]; + } else if (i === j) { + X[i][j] = this.Rdiag[i]; + } else { + X[i][j] = 0; + } + } + } + return X; + }, + get orthogonalFactor() { + var qr = this.QR, + rows = qr.rows, + columns = qr.columns, + X = new Matrix(rows, columns), + i, j, k, s; + + for (k = columns - 1; k >= 0; k--) { + for (i = 0; i < rows; i++) { + X[i][k] = 0; + } + X[k][k] = 1; + for (j = k; j < columns; j++) { + if (qr[k][k] !== 0) { + s = 0; + for (i = k; i < rows; i++) { + s += qr[i][k] * X[i][j]; + } + + s = -s / qr[k][k]; + + for (i = k; i < rows; i++) { + X[i][j] += s * qr[i][k]; + } + } + } + } + return X; + } + }; + + module.exports = QrDecomposition; + + +/***/ }, +/* 121 */ +/***/ function(module, exports, __webpack_require__) { - exports["default"] = XSadd; + 'use strict'; - function period_certification(xsadd) { - if (xsadd.state[0] === 0 && xsadd.state[1] === 0 && xsadd.state[2] === 0 && xsadd.state[3] === 0) { - xsadd.state[0] = 88; // X - xsadd.state[1] = 83; // S - xsadd.state[2] = 65; // A - xsadd.state[3] = 68; // D + var Matrix = __webpack_require__(114); + + // https://github.com/lutzroeder/Mapack/blob/master/Source/CholeskyDecomposition.cs + function CholeskyDecomposition(value) { + if (!(this instanceof CholeskyDecomposition)) { + return new CholeskyDecomposition(value); + } + value = Matrix.checkMatrix(value); + if (!value.isSymmetric()) + throw new Error('Matrix is not symmetric'); + + var a = value, + dimension = a.rows, + l = new Matrix(dimension, dimension), + positiveDefinite = true, + i, j, k; + + for (j = 0; j < dimension; j++) { + var Lrowj = l[j]; + var d = 0; + for (k = 0; k < j; k++) { + var Lrowk = l[k]; + var s = 0; + for (i = 0; i < k; i++) { + s += Lrowk[i] * Lrowj[i]; + } + Lrowj[k] = s = (a[j][k] - s) / l[k][k]; + d = d + s * s; + } + + d = a[j][j] - d; + + positiveDefinite &= (d > 0); + l[j][j] = Math.sqrt(Math.max(d, 0)); + for (k = j + 1; k < dimension; k++) { + l[j][k] = 0; + } + } + + if (!positiveDefinite) { + throw new Error('Matrix is not positive definite'); } + + this.L = l; } - var sh1 = 15; - var sh2 = 18; - var sh3 = 11; - function next_state(xsadd) { - var t = xsadd.state[0]; - t ^= t << sh1; - t ^= t >>> sh2; - t ^= xsadd.state[3] << sh3; - xsadd.state[0] = xsadd.state[1]; - xsadd.state[1] = xsadd.state[2]; - xsadd.state[2] = xsadd.state[3]; - xsadd.state[3] = t; + CholeskyDecomposition.prototype = { + get leftTriangularFactor() { + return this.L; + }, + solve: function (value) { + value = Matrix.checkMatrix(value); + + var l = this.L, + dimension = l.rows; + + if (value.rows !== dimension) { + throw new Error('Matrix dimensions do not match'); + } + + var count = value.columns, + B = value.clone(), + i, j, k; + + for (k = 0; k < dimension; k++) { + for (j = 0; j < count; j++) { + for (i = 0; i < k; i++) { + B[k][j] -= B[i][j] * l[k][i]; + } + B[k][j] /= l[k][k]; + } + } + + for (k = dimension - 1; k >= 0; k--) { + for (j = 0; j < count; j++) { + for (i = k + 1; i < dimension; i++) { + B[k][j] -= B[i][j] * l[i][k]; + } + B[k][j] /= l[k][k]; + } + } + + return B; + } + }; + + module.exports = CholeskyDecomposition; + + +/***/ }, +/* 122 */ +/***/ function(module, exports, __webpack_require__) { + + module.exports = __webpack_require__(123); + + +/***/ }, +/* 123 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + var Matrix = __webpack_require__(14); + var Stat = __webpack_require__(124); + var SVD = Matrix.DC.SVD; + + module.exports = PCA; + + /** + * Creates new PCA (Principal Component Analysis) from the dataset + * @param {Matrix} dataset + * @param {Object} options - options for the PCA algorithm + * @param {boolean} reload - for load purposes + * @param {Object} model - for load purposes + * @constructor + * */ + function PCA(dataset, options, reload, model) { + + if (reload) { + this.U = model.U; + this.S = model.S; + this.means = model.means; + this.std = model.std; + this.standardize = model.standardize + } else { + if(options === undefined) { + options = { + standardize: false + }; + } + + this.standardize = options.standardize; + + if (!Matrix.isMatrix(dataset)) { + dataset = new Matrix(dataset); + } else { + dataset = dataset.clone(); + } + + var normalization = adjust(dataset, this.standardize); + var normalizedDataset = normalization.result; + + var covarianceMatrix = normalizedDataset.transpose().mmul(normalizedDataset).divS(dataset.rows); + + var target = new SVD(covarianceMatrix, { + computeLeftSingularVectors: true, + computeRightSingularVectors: true, + autoTranspose: false + }); + + this.U = target.leftSingularVectors; + this.S = target.diagonal; + this.means = normalization.means; + this.std = normalization.std; + } + } + + /** + * Load a PCA model from JSON + * @oaram {Object} model + * @return {PCA} + * */ + PCA.load = function (model) { + if(model.modelName !== 'PCA') + throw new RangeError("The current model is invalid!"); + + return new PCA(null, null, true, model); + }; + + /** + * Exports the current model to an Object + * @return {Object} model + * */ + PCA.prototype.export = function () { + return { + modelName: "PCA", + U: this.U, + S: this.S, + means: this.means, + std: this.std, + standardize: this.standardize + }; + }; + + /** + * Function that project the dataset into new space of k dimensions, + * this method doesn't modify your dataset. + * @param {Matrix} dataset. + * @param {Number} k - dimensions to project. + * @return {Matrix} dataset projected in k dimensions. + * @throws {RangeError} if k is larger than the number of eigenvector + * of the model. + * */ + PCA.prototype.project = function (dataset, k) { + var dimensions = k - 1; + if(k > this.U.columns) + throw new RangeError("the number of dimensions must not be larger than " + this.U.columns); + + if (!Matrix.isMatrix(dataset)) { + dataset = new Matrix(dataset); + } else { + dataset = dataset.clone(); + } + + var X = adjust(dataset, this.standardize).result; + return X.mmul(this.U.subMatrix(0, this.U.rows - 1, 0, dimensions)); + }; + + /** + * This method returns the percentage variance of each eigenvector. + * @return {Number} percentage variance of each eigenvector. + * */ + PCA.prototype.getExplainedVariance = function () { + var sum = this.S.reduce(function (previous, value) { + return previous + value; + }); + return this.S.map(function (value) { + return value / sum; + }); + }; + + /** + * Function that returns the Eigenvectors of the covariance matrix. + * @returns {Matrix} + */ + PCA.prototype.getEigenvectors = function () { + return this.U; + }; + + /** + * Function that returns the Eigenvalues (on the diagonal). + * @returns {*} + */ + PCA.prototype.getEigenvalues = function () { + return this.S; + }; + + /** + * This method returns a dataset normalized in the following form: + * X = (X - mean) / std + * @param dataset. + * @param {Boolean} standarize - do standardization + * @return A dataset normalized. + * */ + function adjust(dataset, standarize) { + var means = Stat.matrix.mean(dataset); + var std = standarize ? Stat.matrix.standardDeviation(dataset, means, true) : undefined; + + var result = dataset.subRowVector(means); + return { + result: standarize ? result.divRowVector(std) : result, + means: means, + std: std + } } - module.exports = exports["default"]; /***/ }, -/* 108 */ +/* 124 */ /***/ function(module, exports, __webpack_require__) { - module.exports = exports = __webpack_require__(109); - exports.kernel = __webpack_require__(110).kernel; + 'use strict'; + + exports.array = __webpack_require__(125); + exports.matrix = __webpack_require__(126); /***/ }, -/* 109 */ -/***/ function(module, exports, __webpack_require__) { +/* 125 */ +/***/ function(module, exports) { 'use strict'; - var kernel = __webpack_require__(110).kernel; - var getKernel = __webpack_require__(110).getKernel; - - /** - * Parameters to implement function - * @type {{C: number, tol: number, max_passes: number, par: number, k: string}} - * @param {number} C - regularization parameter - * @param {number} tol - numerical tolerance - * @param {number} max_passes - max number of times to iterate over alphas without - * changing - * @param {string} k - the kind of kernel - * @param {number} par - parameter used in the polynomial and the radial function - * of the kernel - */ - var defaultOptions = { - C: 10, - tol: 10e-2, - max_passes: 100, - par: 2, - k: 'lineal' - }; - - /** - * Function to calculate the estimated prediction - * @param {Array } x - point where calculate the function prediction - * @param {Array >} X - training data point in the form (x1, x2) - * @param {Array } Y - training data labels in the domain {1,-1} - * @param {Array } alpha - Lagrange multipliers - * @param {number} b - threshold of the function - * @param {string} k - the kind of kernel - * @param {number} par - parameter used in the polynomial and the radial function - * of the kernel - * @returns {number} - */ - function f(x, X, Y, alpha, b, kernel, par) { - var m = X.length; - var aux = b; - for (var i = 0; i < m; i++) { - b += alpha[i]*Y[i]*kernel(X[i],x, par) - } - return aux; - } - /** - * Simplified version of the Sequential Minimal Optimization algorithm for training - * support vector machines - * @param {{json}} options - parameters to implement function - * @constructor - */ - function SVM(options) { - options = options || {}; - this.options = {}; - for (var o in defaultOptions) { - if (options.hasOwnProperty(o)) { - this.options[o] = options[o]; - } else { - this.options[o] = defaultOptions[o]; - } - } - this.kernel = getKernel(this.options.k); - this.b = 0; + function compareNumbers(a, b) { + return a - b; } /** - * Train the SVM model - * @param {Array >} X - training data point in the form (x1, x2) - * @param {Array } Y - training data labels in the domain {1,-1} + * Computes the sum of the given values + * @param {Array} values + * @returns {number} */ - SVM.prototype.train = function (X, Y) { - var m = Y.length; - var alpha = new Array(m); - for (var a = 0; a < m; a++) - alpha[a] = 0; - if (X.length !== m) - throw new TypeError('Arrays should have the same length'); - var b = 0, - b1 = 0, - b2 = 0, - iter = 0, - Ei = 0, - Ej = 0, - ai = 0, - aj = 0, - L = 0, - H = 0, - eta = 0; - - while (iter < this.options.max_passes) { - var numChange = 0; - for (var i = 0; i < m; i++) { - Ei = f(X[i],X,Y,alpha,b,this.kernel,this.options.par) - Y[i]; - if (((Y[i]*Ei < -this.options.tol) && (alpha[i] < this.options.C)) || ((Y[i]*Ei > this.options.tol) && (alpha[i] > 0))) { - var j = 0; - do { - j = Math.ceil(Math.random()*(m - 1)); - } - while (j === i); - Ej = f(X[j],X,Y,alpha,b,this.kernel,this.options.par) - Y[j]; - ai = alpha[i]; - aj = alpha[j]; - if (Y[i] === Y[j]) { - L = Math.max(0, ai+aj-this.options.C); - H = Math.min(this.options.C, ai+aj); - } - else { - L = Math.max(0, ai-aj); - H = Math.min(this.options.C, this.options.C-ai+aj); - } - if (L !== H) { - eta = 2*this.kernel(X[i],X[j], this.options.par) - this.kernel(X[i],X[i], this.options.par) - this.kernel(X[j],X[j], this.options.par); - if (eta < 0) { - alpha[j] = alpha[j] - (Y[j]*(Ei - Ej)) / eta; - if (alpha[j] > H) - alpha[j] = H; - else if (alpha[j] < L) - alpha[j] = L; - if (Math.abs(aj - alpha[j]) >= 10e-5) { - alpha[i] = alpha[i] + Y[i]*Y[j]*(aj - alpha[j]); - b1 = b - Ei - Y[i]*(alpha[i] - ai)*this.kernel(X[i],X[i], this.options.par) - Y[j]*(alpha[j] - aj)*this.kernel(X[i],X[j], this.options.par); - b2 = b - Ej - Y[i]*(alpha[i] - ai)*this.kernel(X[i],X[j], this.options.par) - Y[j]*(alpha[j] - aj)*this.kernel(X[j],X[j], this.options.par); - if ((alpha[i] < this.options.C) && (alpha[i] > 0)) - b = b1; - else if ((alpha[j] < this.options.C) && (alpha[j] > 0)) - b = b2; - else - b = (b1 + b2) / 2; - numChange += 1; - } - } - } - } - } - if (numChange == 0) - iter += 1; - else - iter = 0; - } - this.b = b; - var s = X[0].length; - this.W = new Array(s); - for (var r = 0; r < s; r++) { - this.W[r] = 0; - for (var w = 0; w < m; w++) - this.W[r] += Y[w]*alpha[w]*X[w][r]; + exports.sum = function sum(values) { + var sum = 0; + for (var i = 0; i < values.length; i++) { + sum += values[i]; } - this.alphas = alpha.splice(); + return sum; }; /** - * Recreates a SVM based in the exported model - * @param {{name: string, ,options: {json} ,alpha: Array, b: number}} model - * @returns {SVM} + * Computes the maximum of the given values + * @param {Array} values + * @returns {number} */ - SVM.load = function (model) { - if (model.name === 'SVM') { - var svm = new SVM(model.options); - svm.W = model.W.slice(); - svm.b = model.b; - return svm; - } else { - throw new TypeError('expecting a SVM model'); + exports.max = function max(values) { + var max = -Infinity; + var l = values.length; + for (var i = 0; i < l; i++) { + if (values[i] > max) max = values[i]; } + return max; }; /** - * Let's have a JSON to recreate the model - * @returns {{name: String("SVM"), ,options: {json} ,alpha: Array, b: number}} - * name identifier, options to recreate model, the Lagrange multipliers and the - * threshold of the objective function - */ - SVM.prototype.export = function () { - var model = { - name: 'SVM' - }; - model.options = this.options; - model.W = this.W; - model.b = this.b; - return model; - }; - - /** - * Return the Lagrange multipliers - * @returns {Array } - */ - SVM.prototype.getAlphas = function () { - return this.alphas.slice(); - }; - - /** - * Returns the threshold of the model function - * @returns {number} threshold of the function - */ - SVM.prototype.getThreshold = function () { - return this.b; - }; - - /** - * Use the train model to make predictions - * @param {Array} p - An array or a single dot to have the prediction - * @returns {*} An array or a single {-1, 1} value of the prediction + * Computes the minimum of the given values + * @param {Array} values + * @returns {number} */ - SVM.prototype.predict = function (p) { - var ev; - if (Array.isArray(p) && (Array.isArray(p[0]) || (typeof p[0] === 'object'))) { - var ans = new Array(p.length); - for (var i = 0; i < ans.length; i++) { - ev = this.b; - for (var j = 0; j < this.W.length; j++) - ev += this.W[j]*p[j]; - if (ev < 0) - ans[i] = -1; - else - ans[i] = 1; - } - return ans; - } - else { - ev = this.b; - for (var e = 0; e < this.W.length; e++) - ev += this.W[e]*p[e]; - if (ev < 0) - return -1; - else - return 1; + exports.min = function min(values) { + var min = Infinity; + var l = values.length; + for (var i = 0; i < l; i++) { + if (values[i] < min) min = values[i]; } + return min; }; - module.exports = SVM; - -/***/ }, -/* 110 */ -/***/ function(module, exports) { - - 'use strict'; - /** - * Kernel function to return the dot product for different spaces - * @param {Array } x1 - input first vector - * @param {Array } x2 - input second vector - * @param {string} func - the kind of transformation - * @param {number} par - parameter used in the polynomial and the radial function - * @return {number} calculus of the dot product using the function - * */ - function kernel(x1,x2,func,par) { - return getKernel(func)(x1, x2, par); - } + * Computes the min and max of the given values + * @param {Array} values + * @returns {{min: number, max: number}} + */ + exports.minMax = function minMax(values) { + var min = Infinity; + var max = -Infinity; + var l = values.length; + for (var i = 0; i < l; i++) { + if (values[i] < min) min = values[i]; + if (values[i] > max) max = values[i]; + } + return { + min: min, + max: max + }; + }; /** - * The dot product between the p1 and p2 vectors - * @param {Array } p1 - first vector to get dot product - * @param {Array } p2 - second vector to get dot product - * @returns {number} dot product between the p1 and p2 vectors + * Computes the arithmetic mean of the given values + * @param {Array} values + * @returns {number} */ - function dot(p1, p2) { - var l = p1.length; - var prod = 0; - + exports.arithmeticMean = function arithmeticMean(values) { + var sum = 0; + var l = values.length; for (var i = 0; i < l; i++) { - prod += p1[i] * p2[i]; + sum += values[i]; } + return sum / l; + }; - return prod; - } - - function getKernel(func) { - func = (typeof func === 'undefined') ? 'lineal' : func; + /** + * {@link arithmeticMean} + */ + exports.mean = exports.arithmeticMean; - switch(func) { - case 'lineal': - return kernelLineal; - case 'polynomial': - return kernelPolynomial; - case 'radial': - return kernelRadial; - default: - throw new TypeError('Function kernel undefined: ' + func); + /** + * Computes the geometric mean of the given values + * @param {Array} values + * @returns {number} + */ + exports.geometricMean = function geometricMean(values) { + var mul = 1; + var l = values.length; + for (var i = 0; i < l; i++) { + mul *= values[i]; } - } - - function kernelLineal(x1,x2) { - return dot(x1,x2); - } - - function kernelPolynomial(x1, x2, par) { - par = (typeof par === 'undefined') ? 2 : par; - return Math.pow((dot(x1, x2) + 1), par); - } + return Math.pow(mul, 1 / l); + }; - function kernelRadial(x1, x2, par) { - par = (typeof par === 'undefined') ? 2 : par; - var l = x1.length; - var rest = new Array(l); + /** + * Computes the mean of the log of the given values + * If the return value is exponentiated, it gives the same result as the + * geometric mean. + * @param {Array} values + * @returns {number} + */ + exports.logMean = function logMean(values) { + var lnsum = 0; + var l = values.length; for (var i = 0; i < l; i++) { - rest[i] = x1[i] - x2[i]; + lnsum += Math.log(values[i]); } - var norm = dot(rest, rest); - return Math.exp((norm)/(-2*par*par)); - } - - module.exports = { - kernel: kernel, - getKernel: getKernel, - lineal : kernelLineal, - polynomial : kernelPolynomial, - radial : kernelRadial + return lnsum / l; }; - -/***/ }, -/* 111 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - module.exports = __webpack_require__(112); - -/***/ }, -/* 112 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - module.exports = KNN; - - var KDTree = __webpack_require__(113).kdTree; - var Distances = __webpack_require__(38); - /** - * K-Nearest neighboor constructor. - * - * @param reload - loading purposes. - * @param model - loading purposes - * @constructor + * Computes the weighted grand mean for a list of means and sample sizes + * @param {Array} means - Mean values for each set of samples + * @param {Array} samples - Number of original values for each set of samples + * @returns {number} */ - function KNN(reload, model) { - if(reload) { - this.kdtree = model.kdtree; - this.k = model.k; - this.classes = model.classes; + exports.grandMean = function grandMean(means, samples) { + var sum = 0; + var n = 0; + var l = means.length; + for (var i = 0; i < l; i++) { + sum += samples[i] * means[i]; + n += samples[i]; } - } + return sum / n; + }; /** - * Function that trains the KNN with the given trainingSet and trainingLabels. - * The third argument is an object with the following options. - * * distance: that represent the distance function applied (default: euclidean) - * * k: the number of neighboors to take in count for classify (default: number of features + 1) - * - * @param trainingSet - * @param trainingLabels - * @param options + * Computes the truncated mean of the given values using a given percentage + * @param {Array} values + * @param {number} percent - The percentage of values to keep (range: [0,1]) + * @param {boolean} [alreadySorted=false] + * @returns {number} */ - KNN.prototype.train = function (trainingSet, trainingLabels, options) { - if(options === undefined) options = {}; - if(options.distance === undefined) options.distance = Distances.distance.euclidean; - if(options.k === undefined) options.k = trainingSet[0].length + 1; + exports.truncatedMean = function truncatedMean(values, percent, alreadySorted) { + if (alreadySorted === undefined) alreadySorted = false; + if (!alreadySorted) { + values = values.slice().sort(compareNumbers); + } + var l = values.length; + var k = Math.floor(l * percent); + var sum = 0; + for (var i = k; i < (l - k); i++) { + sum += values[i]; + } + return sum / (l - 2 * k); + }; - var classes = 0; - var exist = new Array(1000); - var j = 0; - for(var i = 0; i < trainingLabels.length; ++i) { - if(exist.indexOf(trainingLabels[i]) === -1) { - classes++; - exist[j] = trainingLabels[i]; - j++; + /** + * Computes the harmonic mean of the given values + * @param {Array} values + * @returns {number} + */ + exports.harmonicMean = function harmonicMean(values) { + var sum = 0; + var l = values.length; + for (var i = 0; i < l; i++) { + if (values[i] === 0) { + throw new RangeError('value at index ' + i + 'is zero'); } + sum += 1 / values[i]; } + return l / sum; + }; - // copy dataset - var points = new Array(trainingSet.length); - for(i = 0; i < points.length; ++i) { - points[i] = trainingSet[i].slice(); + /** + * Computes the contraharmonic mean of the given values + * @param {Array} values + * @returns {number} + */ + exports.contraHarmonicMean = function contraHarmonicMean(values) { + var r1 = 0; + var r2 = 0; + var l = values.length; + for (var i = 0; i < l; i++) { + r1 += values[i] * values[i]; + r2 += values[i]; } - - this.features = trainingSet[0].length; - for(i = 0; i < trainingLabels.length; ++i) { - points[i].push(trainingLabels[i]); + if (r2 < 0) { + throw new RangeError('sum of values is negative'); } + return r1 / r2; + }; - var dimensions = new Array(trainingSet[0].length); - for(i = 0; i < dimensions.length; ++i) { - dimensions[i] = i; + /** + * Computes the median of the given values + * @param {Array} values + * @param {boolean} [alreadySorted=false] + * @returns {number} + */ + exports.median = function median(values, alreadySorted) { + if (alreadySorted === undefined) alreadySorted = false; + if (!alreadySorted) { + values = values.slice().sort(compareNumbers); + } + var l = values.length; + var half = Math.floor(l / 2); + if (l % 2 === 0) { + return (values[half - 1] + values[half]) * 0.5; + } else { + return values[half]; } - - this.kdtree = new KDTree(points, options.distance, dimensions); - this.k = options.k; - this.classes = classes; }; /** - * Function that returns the predictions given the dataset. - * - * @param dataset - * @returns {Array} + * Computes the variance of the given values + * @param {Array} values + * @param {boolean} [unbiased=true] - if true, divide by (n-1); if false, divide by n. + * @returns {number} */ - KNN.prototype.predict = function (dataset) { - var predictions = new Array(dataset.length); - for(var i = 0; i < dataset.length; ++i) { - predictions[i] = this.getSinglePrediction(dataset[i]); + exports.variance = function variance(values, unbiased) { + if (unbiased === undefined) unbiased = true; + var theMean = exports.mean(values); + var theVariance = 0; + var l = values.length; + + for (var i = 0; i < l; i++) { + var x = values[i] - theMean; + theVariance += x * x; } - return predictions; + if (unbiased) { + return theVariance / (l - 1); + } else { + return theVariance / l; + } }; /** - * function that returns a prediction for a single case. - * @param currentCase + * Computes the standard deviation of the given values + * @param {Array} values + * @param {boolean} [unbiased=true] - if true, divide by (n-1); if false, divide by n. * @returns {number} */ - KNN.prototype.getSinglePrediction = function (currentCase) { - var nearestPoints = this.kdtree.nearest(currentCase, this.k); - var pointsPerClass = new Array(this.classes); - var predictedClass = -1; - var maxPoints = -1; - var lastElement = nearestPoints[0][0].length - 1; + exports.standardDeviation = function standardDeviation(values, unbiased) { + return Math.sqrt(exports.variance(values, unbiased)); + }; - for(var i = 0; i < pointsPerClass.length; ++i) { - pointsPerClass[i] = 0; - } + exports.standardError = function standardError(values) { + return exports.standardDeviation(values) / Math.sqrt(values.length); + }; - for(i = 0; i < nearestPoints.length; ++i) { - var currentClass = nearestPoints[i][0][lastElement]; - var currentPoints = ++pointsPerClass[currentClass]; - if(currentPoints > maxPoints) { - predictedClass = currentClass; - maxPoints = currentPoints; - } + exports.quartiles = function quartiles(values, alreadySorted) { + if (typeof(alreadySorted) === 'undefined') alreadySorted = false; + if (!alreadySorted) { + values = values.slice(); + values.sort(compareNumbers); } - return predictedClass; - }; + var quart = values.length / 4; + var q1 = values[Math.ceil(quart) - 1]; + var q2 = exports.median(values, true); + var q3 = values[Math.ceil(quart * 3) - 1]; - /** - * function that returns a KNN classifier with the given model. - * - * @param model - */ - KNN.load = function (model) { - if(model.modelName !== "KNN") - throw new RangeError("The given model is invalid!"); + return {q1: q1, q2: q2, q3: q3}; + }; - return new KNN(true, model); + exports.pooledStandardDeviation = function pooledStandardDeviation(samples, unbiased) { + return Math.sqrt(exports.pooledVariance(samples, unbiased)); }; - /** - * function that exports the current KNN classifier. - */ - KNN.prototype.export = function () { - return { - modelName: "KNN", - kdtree: this.kdtree, - k: this.k, - classes: this.classes - }; + exports.pooledVariance = function pooledVariance(samples, unbiased) { + if (typeof(unbiased) === 'undefined') unbiased = true; + var sum = 0; + var length = 0, l = samples.length; + for (var i = 0; i < l; i++) { + var values = samples[i]; + var vari = exports.variance(values); + + sum += (values.length - 1) * vari; + + if (unbiased) + length += values.length - 1; + else + length += values.length; + } + return sum / length; }; -/***/ }, -/* 113 */ -/***/ function(module, exports) { + exports.mode = function mode(values) { + var l = values.length, + itemCount = new Array(l), + i; + for (i = 0; i < l; i++) { + itemCount[i] = 0; + } + var itemArray = new Array(l); + var count = 0; - 'use strict'; + for (i = 0; i < l; i++) { + var index = itemArray.indexOf(values[i]); + if (index >= 0) + itemCount[index]++; + else { + itemArray[count] = values[i]; + itemCount[count] = 1; + count++; + } + } - /** - * k-d Tree JavaScript - V 1.01 - * - * https://github.com/ubilabs/kd-tree-javascript - * - * @author Mircea Pricop , 2012 - * @author Martin Kleppe , 2012 - * @author Ubilabs http://ubilabs.net, 2012 - * @license MIT License - */ + var maxValue = 0, maxIndex = 0; + for (i = 0; i < count; i++) { + if (itemCount[i] > maxValue) { + maxValue = itemCount[i]; + maxIndex = i; + } + } + return itemArray[maxIndex]; + }; - function Node(obj, dimension, parent) { - this.obj = obj; - this.left = null; - this.right = null; - this.parent = parent; - this.dimension = dimension; - } + exports.covariance = function covariance(vector1, vector2, unbiased) { + if (typeof(unbiased) === 'undefined') unbiased = true; + var mean1 = exports.mean(vector1); + var mean2 = exports.mean(vector2); - function kdTree(points, metric, dimensions) { + if (vector1.length !== vector2.length) + throw "Vectors do not have the same dimensions"; - var self = this; + var cov = 0, l = vector1.length; + for (var i = 0; i < l; i++) { + var x = vector1[i] - mean1; + var y = vector2[i] - mean2; + cov += x * y; + } - function buildTree(points, depth, parent) { - var dim = depth % dimensions.length, - median, - node; + if (unbiased) + return cov / (l - 1); + else + return cov / l; + }; - if (points.length === 0) { - return null; - } - if (points.length === 1) { - return new Node(points[0], dim, parent); - } + exports.skewness = function skewness(values, unbiased) { + if (typeof(unbiased) === 'undefined') unbiased = true; + var theMean = exports.mean(values); - points.sort(function (a, b) { - return a[dimensions[dim]] - b[dimensions[dim]]; - }); + var s2 = 0, s3 = 0, l = values.length; + for (var i = 0; i < l; i++) { + var dev = values[i] - theMean; + s2 += dev * dev; + s3 += dev * dev * dev; + } + var m2 = s2 / l; + var m3 = s3 / l; - median = Math.floor(points.length / 2); - node = new Node(points[median], dim, parent); - node.left = buildTree(points.slice(0, median), depth + 1, node); - node.right = buildTree(points.slice(median + 1), depth + 1, node); + var g = m3 / (Math.pow(m2, 3 / 2.0)); + if (unbiased) { + var a = Math.sqrt(l * (l - 1)); + var b = l - 2; + return (a / b) * g; + } + else { + return g; + } + }; - return node; + exports.kurtosis = function kurtosis(values, unbiased) { + if (typeof(unbiased) === 'undefined') unbiased = true; + var theMean = exports.mean(values); + var n = values.length, s2 = 0, s4 = 0; + + for (var i = 0; i < n; i++) { + var dev = values[i] - theMean; + s2 += dev * dev; + s4 += dev * dev * dev * dev; + } + var m2 = s2 / n; + var m4 = s4 / n; + + if (unbiased) { + var v = s2 / (n - 1); + var a = (n * (n + 1)) / ((n - 1) * (n - 2) * (n - 3)); + var b = s4 / (v * v); + var c = ((n - 1) * (n - 1)) / ((n - 2) * (n - 3)); + + return a * b - 3 * c; + } + else { + return m4 / (m2 * m2) - 3; } + }; + + exports.entropy = function entropy(values, eps) { + if (typeof(eps) === 'undefined') eps = 0; + var sum = 0, l = values.length; + for (var i = 0; i < l; i++) + sum += values[i] * Math.log(values[i] + eps); + return -sum; + }; + + exports.weightedMean = function weightedMean(values, weights) { + var sum = 0, l = values.length; + for (var i = 0; i < l; i++) + sum += values[i] * weights[i]; + return sum; + }; - // Reloads a serialied tree - function loadTree (data) { - // Just need to restore the `parent` parameter - self.root = data; + exports.weightedStandardDeviation = function weightedStandardDeviation(values, weights) { + return Math.sqrt(exports.weightedVariance(values, weights)); + }; - function restoreParent (root) { - if (root.left) { - root.left.parent = root; - restoreParent(root.left); - } + exports.weightedVariance = function weightedVariance(values, weights) { + var theMean = exports.weightedMean(values, weights); + var vari = 0, l = values.length; + var a = 0, b = 0; - if (root.right) { - root.right.parent = root; - restoreParent(root.right); - } - } + for (var i = 0; i < l; i++) { + var z = values[i] - theMean; + var w = weights[i]; - restoreParent(self.root); + vari += w * (z * z); + b += w; + a += w * w; } - // If points is not an array, assume we're loading a pre-built tree - if (!Array.isArray(points)) loadTree(points, metric, dimensions); - else this.root = buildTree(points, 0, null); + return vari * (b / (b * b - a)); + }; - // Convert to a JSON serializable structure; this just requires removing - // the `parent` property - this.toJSON = function (src) { - if (!src) src = this.root; - var dest = new Node(src.obj, src.dimension, null); - if (src.left) dest.left = self.toJSON(src.left); - if (src.right) dest.right = self.toJSON(src.right); - return dest; - }; + exports.center = function center(values, inPlace) { + if (typeof(inPlace) === 'undefined') inPlace = false; - this.insert = function (point) { - function innerSearch(node, parent) { + var result = values; + if (!inPlace) + result = values.slice(); - if (node === null) { - return parent; - } + var theMean = exports.mean(result), l = result.length; + for (var i = 0; i < l; i++) + result[i] -= theMean; + }; - var dimension = dimensions[node.dimension]; - if (point[dimension] < node.obj[dimension]) { - return innerSearch(node.left, node); - } else { - return innerSearch(node.right, node); - } - } + exports.standardize = function standardize(values, standardDev, inPlace) { + if (typeof(standardDev) === 'undefined') standardDev = exports.standardDeviation(values); + if (typeof(inPlace) === 'undefined') inPlace = false; + var l = values.length; + var result = inPlace ? values : new Array(l); + for (var i = 0; i < l; i++) + result[i] = values[i] / standardDev; + return result; + }; - var insertPosition = innerSearch(this.root, null), - newNode, - dimension; + exports.cumulativeSum = function cumulativeSum(array) { + var l = array.length; + var result = new Array(l); + result[0] = array[0]; + for (var i = 1; i < l; i++) + result[i] = result[i - 1] + array[i]; + return result; + }; - if (insertPosition === null) { - this.root = new Node(point, 0, null); - return; - } - newNode = new Node(point, (insertPosition.dimension + 1) % dimensions.length, insertPosition); - dimension = dimensions[insertPosition.dimension]; +/***/ }, +/* 126 */ +/***/ function(module, exports, __webpack_require__) { - if (point[dimension] < insertPosition.obj[dimension]) { - insertPosition.left = newNode; - } else { - insertPosition.right = newNode; + 'use strict'; + var arrayStat = __webpack_require__(125); + + // https://github.com/accord-net/framework/blob/development/Sources/Accord.Statistics/Tools.cs + + function entropy(matrix, eps) { + if (typeof(eps) === 'undefined') { + eps = 0; + } + var sum = 0, + l1 = matrix.length, + l2 = matrix[0].length; + for (var i = 0; i < l1; i++) { + for (var j = 0; j < l2; j++) { + sum += matrix[i][j] * Math.log(matrix[i][j] + eps); } - }; + } + return -sum; + } - this.remove = function (point) { - var node; + function mean(matrix, dimension) { + if (typeof(dimension) === 'undefined') { + dimension = 0; + } + var rows = matrix.length, + cols = matrix[0].length, + theMean, N, i, j; - function nodeSearch(node) { - if (node === null) { - return null; + if (dimension === -1) { + theMean = [0]; + N = rows * cols; + for (i = 0; i < rows; i++) { + for (j = 0; j < cols; j++) { + theMean[0] += matrix[i][j]; } - - if (node.obj === point) { - return node; + } + theMean[0] /= N; + } else if (dimension === 0) { + theMean = new Array(cols); + N = rows; + for (j = 0; j < cols; j++) { + theMean[j] = 0; + for (i = 0; i < rows; i++) { + theMean[j] += matrix[i][j]; } - - var dimension = dimensions[node.dimension]; - - if (point[dimension] < node.obj[dimension]) { - return nodeSearch(node.left, node); - } else { - return nodeSearch(node.right, node); + theMean[j] /= N; + } + } else if (dimension === 1) { + theMean = new Array(rows); + N = cols; + for (j = 0; j < rows; j++) { + theMean[j] = 0; + for (i = 0; i < cols; i++) { + theMean[j] += matrix[j][i]; } + theMean[j] /= N; } + } else { + throw new Error('Invalid dimension'); + } + return theMean; + } - function removeNode(node) { - var nextNode, - nextObj, - pDimension; - - function findMin(node, dim) { - var dimension, - own, - left, - right, - min; - - if (node === null) { - return null; - } - - dimension = dimensions[dim]; - - if (node.dimension === dim) { - if (node.left !== null) { - return findMin(node.left, dim); - } - return node; - } + function standardDeviation(matrix, means, unbiased) { + var vari = variance(matrix, means, unbiased), l = vari.length; + for (var i = 0; i < l; i++) { + vari[i] = Math.sqrt(vari[i]); + } + return vari; + } - own = node.obj[dimension]; - left = findMin(node.left, dim); - right = findMin(node.right, dim); - min = node; + function variance(matrix, means, unbiased) { + if (typeof(unbiased) === 'undefined') { + unbiased = true; + } + means = means || mean(matrix); + var rows = matrix.length; + if (rows === 0) return []; + var cols = matrix[0].length; + var vari = new Array(cols); - if (left !== null && left.obj[dimension] < own) { - min = left; - } - if (right !== null && right.obj[dimension] < min.obj[dimension]) { - min = right; - } - return min; - } + for (var j = 0; j < cols; j++) { + var sum1 = 0, sum2 = 0, x = 0; + for (var i = 0; i < rows; i++) { + x = matrix[i][j] - means[j]; + sum1 += x; + sum2 += x * x; + } + if (unbiased) { + vari[j] = (sum2 - ((sum1 * sum1) / rows)) / (rows - 1); + } else { + vari[j] = (sum2 - ((sum1 * sum1) / rows)) / rows; + } + } + return vari; + } - if (node.left === null && node.right === null) { - if (node.parent === null) { - self.root = null; - return; - } + function median(matrix) { + var rows = matrix.length, cols = matrix[0].length; + var medians = new Array(cols); - pDimension = dimensions[node.parent.dimension]; + for (var i = 0; i < cols; i++) { + var data = new Array(rows); + for (var j = 0; j < rows; j++) { + data[j] = matrix[j][i]; + } + data.sort(); + var N = data.length; + if (N % 2 === 0) { + medians[i] = (data[N / 2] + data[(N / 2) - 1]) * 0.5; + } else { + medians[i] = data[Math.floor(N / 2)]; + } + } + return medians; + } - if (node.obj[pDimension] < node.parent.obj[pDimension]) { - node.parent.left = null; - } else { - node.parent.right = null; - } - return; - } + function mode(matrix) { + var rows = matrix.length, + cols = matrix[0].length, + modes = new Array(cols), + i, j; + for (i = 0; i < cols; i++) { + var itemCount = new Array(rows); + for (var k = 0; k < rows; k++) { + itemCount[k] = 0; + } + var itemArray = new Array(rows); + var count = 0; - // If the right subtree is not empty, swap with the minimum element on the - // node's dimension. If it is empty, we swap the left and right subtrees and - // do the same. - if (node.right !== null) { - nextNode = findMin(node.right, node.dimension); - nextObj = nextNode.obj; - removeNode(nextNode); - node.obj = nextObj; + for (j = 0; j < rows; j++) { + var index = itemArray.indexOf(matrix[j][i]); + if (index >= 0) { + itemCount[index]++; } else { - nextNode = findMin(node.left, node.dimension); - nextObj = nextNode.obj; - removeNode(nextNode); - node.right = node.left; - node.left = null; - node.obj = nextObj; + itemArray[count] = matrix[j][i]; + itemCount[count] = 1; + count++; } - } - node = nodeSearch(self.root); + var maxValue = 0, maxIndex = 0; + for (j = 0; j < count; j++) { + if (itemCount[j] > maxValue) { + maxValue = itemCount[j]; + maxIndex = j; + } + } - if (node === null) { return; } + modes[i] = itemArray[maxIndex]; + } + return modes; + } - removeNode(node); - }; + function skewness(matrix, unbiased) { + if (typeof(unbiased) === 'undefined') unbiased = true; + var means = mean(matrix); + var n = matrix.length, l = means.length; + var skew = new Array(l); - this.nearest = function (point, maxNodes, maxDistance) { - var i, - result, - bestNodes; + for (var j = 0; j < l; j++) { + var s2 = 0, s3 = 0; + for (var i = 0; i < n; i++) { + var dev = matrix[i][j] - means[j]; + s2 += dev * dev; + s3 += dev * dev * dev; + } - bestNodes = new BinaryHeap( - function (e) { return -e[1]; } - ); + var m2 = s2 / n; + var m3 = s3 / n; + var g = m3 / Math.pow(m2, 3 / 2); - function nearestSearch(node) { - var bestChild, - dimension = dimensions[node.dimension], - ownDistance = metric(point, node.obj), - linearPoint = {}, - linearDistance, - otherChild, - i; + if (unbiased) { + var a = Math.sqrt(n * (n - 1)); + var b = n - 2; + skew[j] = (a / b) * g; + } else { + skew[j] = g; + } + } + return skew; + } - function saveNode(node, distance) { - bestNodes.push([node, distance]); - if (bestNodes.size() > maxNodes) { - bestNodes.pop(); - } - } + function kurtosis(matrix, unbiased) { + if (typeof(unbiased) === 'undefined') unbiased = true; + var means = mean(matrix); + var n = matrix.length, m = matrix[0].length; + var kurt = new Array(m); - for (i = 0; i < dimensions.length; i += 1) { - if (i === node.dimension) { - linearPoint[dimensions[i]] = point[dimensions[i]]; - } else { - linearPoint[dimensions[i]] = node.obj[dimensions[i]]; - } - } + for (var j = 0; j < m; j++) { + var s2 = 0, s4 = 0; + for (var i = 0; i < n; i++) { + var dev = matrix[i][j] - means[j]; + s2 += dev * dev; + s4 += dev * dev * dev * dev; + } + var m2 = s2 / n; + var m4 = s4 / n; - linearDistance = metric(linearPoint, node.obj); + if (unbiased) { + var v = s2 / (n - 1); + var a = (n * (n + 1)) / ((n - 1) * (n - 2) * (n - 3)); + var b = s4 / (v * v); + var c = ((n - 1) * (n - 1)) / ((n - 2) * (n - 3)); + kurt[j] = a * b - 3 * c; + } else { + kurt[j] = m4 / (m2 * m2) - 3; + } + } + return kurt; + } - if (node.right === null && node.left === null) { - if (bestNodes.size() < maxNodes || ownDistance < bestNodes.peek()[1]) { - saveNode(node, ownDistance); - } - return; - } + function standardError(matrix) { + var samples = matrix.length; + var standardDeviations = standardDeviation(matrix), l = standardDeviations.length; + var standardErrors = new Array(l); + var sqrtN = Math.sqrt(samples); - if (node.right === null) { - bestChild = node.left; - } else if (node.left === null) { - bestChild = node.right; - } else { - if (point[dimension] < node.obj[dimension]) { - bestChild = node.left; - } else { - bestChild = node.right; - } - } + for (var i = 0; i < l; i++) { + standardErrors[i] = standardDeviations[i] / sqrtN; + } + return standardErrors; + } - nearestSearch(bestChild); + function covariance(matrix, dimension) { + return scatter(matrix, undefined, dimension); + } - if (bestNodes.size() < maxNodes || ownDistance < bestNodes.peek()[1]) { - saveNode(node, ownDistance); - } + function scatter(matrix, divisor, dimension) { + if (typeof(dimension) === 'undefined') { + dimension = 0; + } + if (typeof(divisor) === 'undefined') { + if (dimension === 0) { + divisor = matrix.length - 1; + } else if (dimension === 1) { + divisor = matrix[0].length - 1; + } + } + var means = mean(matrix, dimension), + rows = matrix.length; + if (rows === 0) { + return [[]]; + } + var cols = matrix[0].length, + cov, i, j, s, k; - if (bestNodes.size() < maxNodes || Math.abs(linearDistance) < bestNodes.peek()[1]) { - if (bestChild === node.left) { - otherChild = node.right; - } else { - otherChild = node.left; + if (dimension === 0) { + cov = new Array(cols); + for (i = 0; i < cols; i++) { + cov[i] = new Array(cols); + } + for (i = 0; i < cols; i++) { + for (j = i; j < cols; j++) { + s = 0; + for (k = 0; k < rows; k++) { + s += (matrix[k][j] - means[j]) * (matrix[k][i] - means[i]); } - if (otherChild !== null) { - nearestSearch(otherChild); + s /= divisor; + cov[i][j] = s; + cov[j][i] = s; + } + } + } else if (dimension === 1) { + cov = new Array(rows); + for (i = 0; i < rows; i++) { + cov[i] = new Array(rows); + } + for (i = 0; i < rows; i++) { + for (j = i; j < rows; j++) { + s = 0; + for (k = 0; k < cols; k++) { + s += (matrix[j][k] - means[j]) * (matrix[i][k] - means[i]); } + s /= divisor; + cov[i][j] = s; + cov[j][i] = s; } } + } else { + throw new Error('Invalid dimension'); + } - if (maxDistance) { - for (i = 0; i < maxNodes; i += 1) { - bestNodes.push([null, maxDistance]); + return cov; + } + + function correlation(matrix) { + var means = mean(matrix), + standardDeviations = standardDeviation(matrix, true, means), + scores = zScores(matrix, means, standardDeviations), + rows = matrix.length, + cols = matrix[0].length, + i, j; + + var cor = new Array(cols); + for (i = 0; i < cols; i++) { + cor[i] = new Array(cols); + } + for (i = 0; i < cols; i++) { + for (j = i; j < cols; j++) { + var c = 0; + for (var k = 0, l = scores.length; k < l; k++) { + c += scores[k][j] * scores[k][i]; } + c /= rows - 1; + cor[i][j] = c; + cor[j][i] = c; } + } + return cor; + } - if(self.root) - nearestSearch(self.root); + function zScores(matrix, means, standardDeviations) { + means = means || mean(matrix); + if (typeof(standardDeviations) === 'undefined') standardDeviations = standardDeviation(matrix, true, means); + return standardize(center(matrix, means, false), standardDeviations, true); + } - result = []; + function center(matrix, means, inPlace) { + means = means || mean(matrix); + var result = matrix, + l = matrix.length, + i, j, jj; - for (i = 0; i < Math.min(maxNodes, bestNodes.content.length); i += 1) { - if (bestNodes.content[i][0]) { - result.push([bestNodes.content[i][0].obj, bestNodes.content[i][1]]); - } + if (!inPlace) { + result = new Array(l); + for (i = 0; i < l; i++) { + result[i] = new Array(matrix[i].length); } - return result; - }; + } - this.balanceFactor = function () { - function height(node) { - if (node === null) { - return 0; - } - return Math.max(height(node.left), height(node.right)) + 1; + for (i = 0; i < l; i++) { + var row = result[i]; + for (j = 0, jj = row.length; j < jj; j++) { + row[j] = matrix[i][j] - means[j]; } + } + return result; + } - function count(node) { - if (node === null) { - return 0; - } - return count(node.left) + count(node.right) + 1; + function standardize(matrix, standardDeviations, inPlace) { + if (typeof(standardDeviations) === 'undefined') standardDeviations = standardDeviation(matrix); + var result = matrix, + l = matrix.length, + i, j, jj; + + if (!inPlace) { + result = new Array(l); + for (i = 0; i < l; i++) { + result[i] = new Array(matrix[i].length); } + } - return height(self.root) / (Math.log(count(self.root)) / Math.log(2)); - }; + for (i = 0; i < l; i++) { + var resultRow = result[i]; + var sourceRow = matrix[i]; + for (j = 0, jj = resultRow.length; j < jj; j++) { + if (standardDeviations[j] !== 0 && !isNaN(standardDeviations[j])) { + resultRow[j] = sourceRow[j] / standardDeviations[j]; + } + } + } + return result; } - // Binary heap implementation from: - // http://eloquentjavascript.net/appendix2.html + function weightedVariance(matrix, weights) { + var means = mean(matrix); + var rows = matrix.length; + if (rows === 0) return []; + var cols = matrix[0].length; + var vari = new Array(cols); - function BinaryHeap(scoreFunction){ - this.content = []; - this.scoreFunction = scoreFunction; - } + for (var j = 0; j < cols; j++) { + var sum = 0; + var a = 0, b = 0; - BinaryHeap.prototype = { - push: function(element) { - // Add the new element to the end of the array. - this.content.push(element); - // Allow it to bubble up. - this.bubbleUp(this.content.length - 1); - }, + for (var i = 0; i < rows; i++) { + var z = matrix[i][j] - means[j]; + var w = weights[i]; - pop: function() { - // Store the first element so we can return it later. - var result = this.content[0]; - // Get the element at the end of the array. - var end = this.content.pop(); - // If there are any elements left, put the end element at the - // start, and let it sink down. - if (this.content.length > 0) { - this.content[0] = end; - this.sinkDown(0); + sum += w * (z * z); + b += w; + a += w * w; } - return result; - }, - peek: function() { - return this.content[0]; - }, + vari[j] = sum * (b / (b * b - a)); + } - remove: function(node) { - var len = this.content.length; - // To remove a value, we must search through the array to find - // it. - for (var i = 0; i < len; i++) { - if (this.content[i] == node) { - // When it is found, the process seen in 'pop' is repeated - // to fill up the hole. - var end = this.content.pop(); - if (i != len - 1) { - this.content[i] = end; - if (this.scoreFunction(end) < this.scoreFunction(node)) - this.bubbleUp(i); - else - this.sinkDown(i); - } - return; - } - } - throw new Error("Node not found."); - }, + return vari; + } - size: function() { - return this.content.length; - }, + function weightedMean(matrix, weights, dimension) { + if (typeof(dimension) === 'undefined') { + dimension = 0; + } + var rows = matrix.length; + if (rows === 0) return []; + var cols = matrix[0].length, + means, i, ii, j, w, row; - bubbleUp: function(n) { - // Fetch the element that has to be moved. - var element = this.content[n]; - // When at 0, an element can not go up any further. - while (n > 0) { - // Compute the parent element's index, and fetch it. - var parentN = Math.floor((n + 1) / 2) - 1, - parent = this.content[parentN]; - // Swap the elements if the parent is greater. - if (this.scoreFunction(element) < this.scoreFunction(parent)) { - this.content[parentN] = element; - this.content[n] = parent; - // Update 'n' to continue at the new position. - n = parentN; + if (dimension === 0) { + means = new Array(cols); + for (i = 0; i < cols; i++) { + means[i] = 0; + } + for (i = 0; i < rows; i++) { + row = matrix[i]; + w = weights[i]; + for (j = 0; j < cols; j++) { + means[j] += row[j] * w; } - // Found a parent that is less, no need to move it further. - else { - break; + } + } else if (dimension === 1) { + means = new Array(rows); + for (i = 0; i < rows; i++) { + means[i] = 0; + } + for (j = 0; j < rows; j++) { + row = matrix[j]; + w = weights[j]; + for (i = 0; i < cols; i++) { + means[j] += row[i] * w; } } - }, + } else { + throw new Error('Invalid dimension'); + } - sinkDown: function(n) { - // Look up the target element and its score. - var length = this.content.length, - element = this.content[n], - elemScore = this.scoreFunction(element); + var weightSum = arrayStat.sum(weights); + if (weightSum !== 0) { + for (i = 0, ii = means.length; i < ii; i++) { + means[i] /= weightSum; + } + } + return means; + } - while(true) { - // Compute the indices of the child elements. - var child2N = (n + 1) * 2, child1N = child2N - 1; - // This is used to store the new position of the element, - // if any. - var swap = null; - // If the first child exists (is inside the array)... - if (child1N < length) { - // Look it up and compute its score. - var child1 = this.content[child1N], - child1Score = this.scoreFunction(child1); - // If the score is less than our element's, we need to swap. - if (child1Score < elemScore) - swap = child1N; - } - // Do the same checks for the other child. - if (child2N < length) { - var child2 = this.content[child2N], - child2Score = this.scoreFunction(child2); - if (child2Score < (swap == null ? elemScore : child1Score)){ - swap = child2N; + function weightedCovariance(matrix, weights, means, dimension) { + dimension = dimension || 0; + means = means || weightedMean(matrix, weights, dimension); + var s1 = 0, s2 = 0; + for (var i = 0, ii = weights.length; i < ii; i++) { + s1 += weights[i]; + s2 += weights[i] * weights[i]; + } + var factor = s1 / (s1 * s1 - s2); + return weightedScatter(matrix, weights, means, factor, dimension); + } + + function weightedScatter(matrix, weights, means, factor, dimension) { + dimension = dimension || 0; + means = means || weightedMean(matrix, weights, dimension); + if (typeof(factor) === 'undefined') { + factor = 1; + } + var rows = matrix.length; + if (rows === 0) { + return [[]]; + } + var cols = matrix[0].length, + cov, i, j, k, s; + + if (dimension === 0) { + cov = new Array(cols); + for (i = 0; i < cols; i++) { + cov[i] = new Array(cols); + } + for (i = 0; i < cols; i++) { + for (j = i; j < cols; j++) { + s = 0; + for (k = 0; k < rows; k++) { + s += weights[k] * (matrix[k][j] - means[j]) * (matrix[k][i] - means[i]); } + cov[i][j] = s * factor; + cov[j][i] = s * factor; } - - // If the element needs to be moved, swap it, and continue. - if (swap != null) { - this.content[n] = this.content[swap]; - this.content[swap] = element; - n = swap; - } - // Otherwise, we are done. - else { - break; + } + } else if (dimension === 1) { + cov = new Array(rows); + for (i = 0; i < rows; i++) { + cov[i] = new Array(rows); + } + for (i = 0; i < rows; i++) { + for (j = i; j < rows; j++) { + s = 0; + for (k = 0; k < cols; k++) { + s += weights[k] * (matrix[j][k] - means[j]) * (matrix[i][k] - means[i]); + } + cov[i][j] = s * factor; + cov[j][i] = s * factor; } } + } else { + throw new Error('Invalid dimension'); } - }; - this.kdTree = kdTree; + return cov; + } - exports.kdTree = kdTree; - exports.BinaryHeap = BinaryHeap; + module.exports = { + entropy: entropy, + mean: mean, + standardDeviation: standardDeviation, + variance: variance, + median: median, + mode: mode, + skewness: skewness, + kurtosis: kurtosis, + standardError: standardError, + covariance: covariance, + scatter: scatter, + correlation: correlation, + zScores: zScores, + center: center, + standardize: standardize, + weightedVariance: weightedVariance, + weightedMean: weightedMean, + weightedCovariance: weightedCovariance, + weightedScatter: weightedScatter + }; /***/ }, -/* 114 */ +/* 127 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; - module.exports = exports = __webpack_require__(115).NaiveBayes; - exports.separateClasses = __webpack_require__(115).separateClasses; - + const measures = __webpack_require__(128); -/***/ }, -/* 115 */ -/***/ function(module, exports, __webpack_require__) { + class Performance { + /** + * + * @param prediction - The prediction matrix + * @param target - The target matrix (values: truthy for same class, falsy for different class) + * @param options + * + * @option all True if the entire matrix must be used. False to ignore the diagonal and lower part (default is false, for similarity/distance matrices) + * @option max True if the max value corresponds to a perfect match (like in similarity matrices), false if it is the min value (default is false, like in distance matrices. All values will be multiplied by -1) + */ + constructor(prediction, target, options) { + options = options || {}; + if (prediction.length !== target.length || prediction[0].length !== target[0].length) { + throw new Error('dimensions of prediction and target do not match'); + } + const rows = prediction.length; + const columns = prediction[0].length; + const neg = !options.max; - 'use strict'; + const predP = []; - var Matrix = __webpack_require__(14); - var Stat = __webpack_require__(116); + if (options.all) { + for (var i = 0; i < rows; i++) { + for (var j = 0; j < columns; j++) { + predP.push({ + pred: neg ? 0 - prediction[i][j] : prediction[i][j], + targ: target[i][j] + }); + } + } + } else { + if (rows < 3 || rows !== columns) { + throw new Error('When "all" option is false, the prediction matrix must be square and have at least 3 columns'); + } + for (var i = 0; i < rows - 1; i++) { + for (var j = i + 1; j < columns; j++) { + predP.push({ + pred: neg ? 0 - prediction[i][j] : prediction[i][j], + targ: target[i][j] + }); + } + } + } - module.exports.NaiveBayes = NaiveBayes; - module.exports.separateClasses = separateClasses; + predP.sort((a, b) => b.pred - a.pred); - /** - * Constructor for the Naive Bayes classifier, the parameters here is just for loading purposes. - * - * @param reload - * @param model - * @constructor - */ - function NaiveBayes(reload, model) { - if(reload) { - this.means = model.means; - this.calculateProbabilities = model.calculateProbabilities; - } - } + const cutoffs = this.cutoffs = [Number.MAX_VALUE]; + const fp = this.fp = [0]; + const tp = this.tp = [0]; - /** - * Function that trains the classifier with a matrix that represents the training set and an array that - * represents the label of each row in the training set. the labels must be numbers between 0 to n-1 where - * n represents the number of classes. - * - * WARNING: in the case that one class, all the cases in one or more features have the same value, the - * Naive Bayes classifier will not work well. - * @param trainingSet - * @param trainingLabels - */ - NaiveBayes.prototype.train = function (trainingSet, trainingLabels) { - var C1 = Math.sqrt(2*Math.PI); // constant to precalculate the squared root - if(!Matrix.isMatrix(trainingSet)) trainingSet = new Matrix(trainingSet); - else trainingSet = trainingSet.clone(); + var nPos = 0; + var nNeg = 0; - if(trainingSet.rows !== trainingLabels.length) - throw new RangeError("the size of the training set and the training labels must be the same."); + var currentPred = predP[0].pred; + var nTp = 0; + var nFp = 0; + for (var i = 0; i < predP.length; i++) { + if (predP[i].pred !== currentPred) { + cutoffs.push(currentPred); + fp.push(nFp); + tp.push(nTp); + currentPred = predP[i].pred; + } + if (predP[i].targ) { + nPos++; + nTp++; + } else { + nNeg++; + nFp++; + } + } + cutoffs.push(currentPred); + fp.push(nFp); + tp.push(nTp); - var separatedClasses = separateClasses(trainingSet, trainingLabels); - var calculateProbabilities = new Array(separatedClasses.length); - this.means = new Array(separatedClasses.length); - for(var i = 0; i < separatedClasses.length; ++i) { - var means = Stat.matrix.mean(separatedClasses[i]); - var std = Stat.matrix.standardDeviation(separatedClasses[i], means); + const l = cutoffs.length; + const fn = this.fn = new Array(l); + const tn = this.tn = new Array(l); + const nPosPred = this.nPosPred = new Array(l); + const nNegPred = this.nNegPred = new Array(l); - var logPriorProbability = Math.log(separatedClasses[i].rows / trainingSet.rows); - calculateProbabilities[i] = new Array(means.length + 1); + for (var i = 0; i < l; i++) { + fn[i] = nPos - tp[i]; + tn[i] = nNeg - fp[i]; - calculateProbabilities[i][0] = logPriorProbability; - for(var j = 1; j < means.length + 1; ++j) { - var currentStd = std[j - 1]; - calculateProbabilities[i][j] = [(1 / (C1 * currentStd)), -2*currentStd*currentStd]; + nPosPred[i] = tp[i] + fp[i]; + nNegPred[i] = tn[i] + fn[i]; } - this.means[i] = means; + this.nPos = nPos; + this.nNeg = nNeg; + this.nSamples = nPos + nNeg; } - this.calculateProbabilities = calculateProbabilities; - }; - - /** - * function that predicts each row of the dataset (must be a matrix). - * - * @param dataset - * @returns {Array} - */ - NaiveBayes.prototype.predict = function (dataset) { - if(dataset[0].length === this.calculateProbabilities[0].length) - throw new RangeError('the dataset must have the same features as the training set'); - - var predictions = new Array(dataset.length); - - for(var i = 0; i < predictions.length; ++i) { - predictions[i] = getCurrentClass(dataset[i], this.means, this.calculateProbabilities); + /** + * Computes a measure from the prediction object. + * + * Many measures are available and can be combined : + * To create a ROC curve, you need fpr and tpr + * To create a DET curve, you need fnr and fpr + * To create a Lift chart, you need rpp and lift + * + * Possible measures are : threshold (Threshold), acc (Accuracy), err (Error rate), + * fpr (False positive rate), tpr (True positive rate), fnr (False negative rate), tnr (True negative rate), ppv (Positive predictive value), + * npv (Negative predictive value), pcfall (Prediction-conditioned fallout), pcmiss (Prediction-conditioned miss), lift (Lift value), rpp (Rate of positive predictions), rnp (Rate of negative predictions) + * + * @param measure - The short name of the measure + * + * @return [number] + */ + getMeasure(measure) { + if (typeof measure !== 'string') { + throw new Error('No measure specified'); + } + if (!measures[measure]) { + throw new Error(`The specified measure (${measure}) does not exist`); + } + return measures[measure](this); } - return predictions; - }; - - /** - * Function the retrieves a prediction with one case. - * - * @param currentCase - * @param mean - Precalculated means of each class trained - * @param classes - Precalculated value of each class (Prior probability and probability function of each feature) - * @returns {number} - */ - function getCurrentClass(currentCase, mean, classes) { - var maxProbability = 0; - var predictedClass = -1; - - // going through all precalculated values for the classes - for(var i = 0; i < classes.length; ++i) { - var currentProbability = classes[i][0]; // initialize with the prior probability - for(var j = 1; j < classes[0][1].length + 1; ++j) { - currentProbability += calculateLogProbability(currentCase[j - 1], mean[i][j - 1], classes[i][j][0], classes[i][j][1]); + /** + * Returns the area under the ROC curve + */ + getAURC() { + const l = this.cutoffs.length; + const x = new Array(l); + const y = new Array(l); + for (var i = 0; i < l; i++) { + x[i] = this.fp[i] / this.nNeg; + y[i] = this.tp[i] / this.nPos; + } + var auc = 0; + for (i = 1; i < l; i++) { + auc += 0.5 * (x[i] - x[i - 1]) * (y[i] + y[i - 1]); } + return auc; + } - currentProbability = Math.exp(currentProbability); - if(currentProbability > maxProbability) { - maxProbability = currentProbability; - predictedClass = i; + /** + * Returns the area under the DET curve + */ + getAUDC() { + const l = this.cutoffs.length; + const x = new Array(l); + const y = new Array(l); + for (var i = 0; i < l; i++) { + x[i] = this.fn[i] / this.nPos; + y[i] = this.fp[i] / this.nNeg; + } + var auc = 0; + for (i = 1; i < l; i++) { + auc += 0.5 * (x[i] + x[i - 1]) * (y[i] - y[i - 1]); } + return auc; } - return predictedClass; - } + getDistribution(options) { + options = options || {}; + var cutLength = this.cutoffs.length; + var cutLow = options.xMin || Math.floor(this.cutoffs[cutLength - 1] * 100) / 100; + var cutHigh = options.xMax || Math.ceil(this.cutoffs[1] * 100) / 100; + var interval = options.interval || Math.floor(((cutHigh - cutLow) / 20 * 10000000) - 1) / 10000000; // Trick to avoid the precision problem of float numbers - /** - * Function that export the NaiveBayes model. - * @returns {{modelName: string, means: *, calculateProbabilities: *}} - */ - NaiveBayes.prototype.export = function () { - return { - modelName: "NaiveBayes", - means: this.means, - calculateProbabilities: this.calculateProbabilities - }; - }; + var xLabels = []; + var interValues = []; + var intraValues = []; + var interCumPercent = []; + var intraCumPercent = []; - /** - * Function that create a Naive Bayes classifier with the given model. - * @param model - * @returns {NaiveBayes} - */ - NaiveBayes.load = function (model) { - if(model.modelName !== 'NaiveBayes') - throw new RangeError("The given model is invalid!"); + var nTP = this.tp[cutLength - 1], currentTP = 0; + var nFP = this.fp[cutLength - 1], currentFP = 0; - return new NaiveBayes(true, model); - }; + for (var i = cutLow, j = (cutLength - 1); i <= cutHigh; i += interval) { + while (this.cutoffs[j] < i) + j--; - /** - * function that retrieves the probability of the feature given the class. - * @param value - value of the feature. - * @param mean - mean of the feature for the given class. - * @param C1 - precalculated value of (1 / (sqrt(2*pi) * std)). - * @param C2 - precalculated value of (2 * std^2) for the denominator of the exponential. - * @returns {number} - */ - function calculateLogProbability(value, mean, C1, C2) { - var value = value - mean; - return Math.log(C1 * Math.exp((value * value) / C2)) - } + xLabels.push(i); - /** - * Function that retuns an array of matrices of the cases that belong to each class. - * @param X - dataset - * @param y - predictions - * @returns {Array} - */ - function separateClasses(X, y) { - var features = X.columns; + var thisTP = nTP - currentTP - this.tp[j]; + var thisFP = nFP - currentFP - this.fp[j]; - var classes = 0; - var totalPerClasses = new Array(100); // max upperbound of classes - for (var i = 0; i < y.length; i++) { - if(totalPerClasses[y[i]] === undefined) { - totalPerClasses[y[i]] = 0; - classes++; + currentTP += thisTP; + currentFP += thisFP; + + interValues.push(thisFP); + intraValues.push(thisTP); + + interCumPercent.push(100 - (nFP - this.fp[j]) / nFP * 100); + intraCumPercent.push(100 - (nTP - this.tp[j]) / nTP * 100); } - totalPerClasses[y[i]]++; - } - var separatedClasses = new Array(classes); - var currentIndex = new Array(classes); - for(i = 0; i < classes; ++i) { - separatedClasses[i] = new Matrix(totalPerClasses[i], features); - currentIndex[i] = 0; - } - for(i = 0; i < X.rows; ++i) { - separatedClasses[y[i]].setRow(currentIndex[y[i]], X.getRow(i)); - currentIndex[y[i]]++; + + return { + xLabels: xLabels, + interValues: interValues, + intraValues: intraValues, + interCumPercent: interCumPercent, + intraCumPercent: intraCumPercent + }; } - return separatedClasses; } + Performance.names = { + acc: 'Accuracy', + err: 'Error rate', + fpr: 'False positive rate', + tpr: 'True positive rate', + fnr: 'False negative rate', + tnr: 'True negative rate', + ppv: 'Positive predictive value', + npv: 'Negative predictive value', + pcfall: 'Prediction-conditioned fallout', + pcmiss: 'Prediction-conditioned miss', + lift: 'Lift value', + rpp: 'Rate of positive predictions', + rnp: 'Rate of negative predictions', + threshold: 'Threshold' + }; + + module.exports = Performance; + /***/ }, -/* 116 */ -/***/ function(module, exports, __webpack_require__) { +/* 128 */ +/***/ function(module, exports) { 'use strict'; - exports.array = __webpack_require__(117); - exports.matrix = __webpack_require__(118); + // Accuracy + exports.acc = pred => { + const l = pred.cutoffs.length; + const result = new Array(l); + for (var i = 0; i < l; i++) { + result[i] = (pred.tn[i] + pred.tp[i]) / (l - 1); + } + return result; + }; + // Error rate + exports.err = pred => { + const l = pred.cutoffs.length; + const result = new Array(l); + for (var i = 0; i < l; i++) { + result[i] = (pred.fn[i] + pred.fp[i] / (l - 1)); + } + return result; + }; -/***/ }, -/* 117 */ -/***/ function(module, exports) { + // False positive rate + exports.fpr = pred => { + const l = pred.cutoffs.length; + const result = new Array(l); + for (var i = 0; i < l; i++) { + result[i] = pred.fp[i] / pred.nNeg; + } + return result; + }; - 'use strict'; + // True positive rate + exports.tpr = pred => { + const l = pred.cutoffs.length; + const result = new Array(l); + for (var i = 0; i < l; i++) { + result[i] = pred.tp[i] / pred.nPos; + } + return result; + }; - function compareNumbers(a, b) { - return a - b; - } + // False negative rate + exports.fnr = pred => { + const l = pred.cutoffs.length; + const result = new Array(l); + for (var i = 0; i < l; i++) { + result[i] = pred.fn[i] / pred.nPos; + } + return result; + }; - /** - * Computes the sum of the given values - * @param {Array} values - * @returns {number} - */ - exports.sum = function sum(values) { - var sum = 0; - for (var i = 0; i < values.length; i++) { - sum += values[i]; + // True negative rate + exports.tnr = pred => { + const l = pred.cutoffs.length; + const result = new Array(l); + for (var i = 0; i < l; i++) { + result[i] = pred.tn[i] / pred.nNeg; } - return sum; + return result; }; - /** - * Computes the maximum of the given values - * @param {Array} values - * @returns {number} - */ - exports.max = function max(values) { - var max = -Infinity; - var l = values.length; + // Positive predictive value + exports.ppv = pred => { + const l = pred.cutoffs.length; + const result = new Array(l); for (var i = 0; i < l; i++) { - if (values[i] > max) max = values[i]; + result[i] = (pred.fp[i] + pred.tp[i] !== 0) ? (pred.tp[i] / (pred.fp[i] + pred.tp[i])) : 0; } - return max; + return result; }; - /** - * Computes the minimum of the given values - * @param {Array} values - * @returns {number} - */ - exports.min = function min(values) { - var min = Infinity; - var l = values.length; + // Negative predictive value + exports.npv = pred => { + const l = pred.cutoffs.length; + const result = new Array(l); for (var i = 0; i < l; i++) { - if (values[i] < min) min = values[i]; + result[i] = (pred.fn[i] + pred.tn[i] !== 0) ? (pred.tn[i] / (pred.fn[i] + pred.tn[i])) : 0; + } + return result; + }; + + // Prediction conditioned fallout + exports.pcfall = pred => { + const l = pred.cutoffs.length; + const result = new Array(l); + for (var i = 0; i < l; i++) { + result[i] = (pred.fp[i] + pred.tp[i] !== 0) ? 1 - (pred.tp[i] / (pred.fp[i] + pred.tp[i])) : 1; } - return min; + return result; }; - /** - * Computes the min and max of the given values - * @param {Array} values - * @returns {{min: number, max: number}} - */ - exports.minMax = function minMax(values) { - var min = Infinity; - var max = -Infinity; - var l = values.length; + // Prediction conditioned miss + exports.pcmiss = pred => { + const l = pred.cutoffs.length; + const result = new Array(l); for (var i = 0; i < l; i++) { - if (values[i] < min) min = values[i]; - if (values[i] > max) max = values[i]; + result[i] = (pred.fn[i] + pred.tn[i] !== 0) ? 1 - (pred.tn[i] / (pred.fn[i] + pred.tn[i])) : 1; } - return { - min: min, - max: max - }; + return result; }; - /** - * Computes the arithmetic mean of the given values - * @param {Array} values - * @returns {number} - */ - exports.arithmeticMean = function arithmeticMean(values) { - var sum = 0; - var l = values.length; + // Lift value + exports.lift = pred => { + const l = pred.cutoffs.length; + const result = new Array(l); for (var i = 0; i < l; i++) { - sum += values[i]; + result[i] = (pred.nPosPred[i] !== 0) ? ((pred.tp[i] / pred.nPos) / (pred.nPosPred[i] / pred.nSamples)) : 0; } - return sum / l; + return result; }; - /** - * {@link arithmeticMean} - */ - exports.mean = exports.arithmeticMean; - - /** - * Computes the geometric mean of the given values - * @param {Array} values - * @returns {number} - */ - exports.geometricMean = function geometricMean(values) { - var mul = 1; - var l = values.length; + // Rate of positive predictions + exports.rpp = pred => { + const l = pred.cutoffs.length; + const result = new Array(l); for (var i = 0; i < l; i++) { - mul *= values[i]; + result[i] = pred.nPosPred[i] / pred.nSamples; } - return Math.pow(mul, 1 / l); + return result; }; - /** - * Computes the mean of the log of the given values - * If the return value is exponentiated, it gives the same result as the - * geometric mean. - * @param {Array} values - * @returns {number} - */ - exports.logMean = function logMean(values) { - var lnsum = 0; - var l = values.length; + // Rate of negative predictions + exports.rnp = pred => { + const l = pred.cutoffs.length; + const result = new Array(l); for (var i = 0; i < l; i++) { - lnsum += Math.log(values[i]); + result[i] = pred.nNegPred[i] / pred.nSamples; } - return lnsum / l; + return result; + }; + + // Threshold + exports.threshold = pred => { + const clone = pred.cutoffs.slice(); + clone[0] = clone[1]; // Remove the infinite value + return clone; }; + +/***/ }, +/* 129 */ +/***/ function(module, exports) { + + "use strict"; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + var LOOP = 8; + var FLOAT_MUL = 1 / 16777216; + + function multiply_uint32(n, m) { + n >>>= 0; + m >>>= 0; + var nlo = n & 0xffff; + var nhi = n - nlo; + return (nhi * m >>> 0) + nlo * m >>> 0; + } + + var XSadd = (function () { + function XSadd() { + var seed = arguments.length <= 0 || arguments[0] === undefined ? Date.now() : arguments[0]; + + _classCallCheck(this, XSadd); + + this.state = new Uint32Array(4); + this.init(seed); + } + + _createClass(XSadd, [{ + key: "init", + value: function init(seed) { + this.state[0] = seed; + this.state[1] = 0; + this.state[2] = 0; + this.state[3] = 0; + for (var i = 1; i < LOOP; i++) { + this.state[i & 3] ^= i + multiply_uint32(1812433253, this.state[i - 1 & 3] ^ this.state[i - 1 & 3] >>> 30 >>> 0) >>> 0; + } + period_certification(this); + for (var i = 0; i < LOOP; i++) { + next_state(this); + } + } + + /** + * Returns a 32-bit integer r (0 <= r < 2^32) + */ + }, { + key: "getUint32", + value: function getUint32() { + next_state(this); + return this.state[3] + this.state[2] >>> 0; + } + + /** + * Returns a floating point number r (0.0 <= r < 1.0) + */ + }, { + key: "getFloat", + value: function getFloat() { + return (this.getUint32() >>> 8) * FLOAT_MUL; + } + }, { + key: "random", + get: function get() { + if (!this._random) { + this._random = this.getFloat.bind(this); + } + return this._random; + } + }]); + + return XSadd; + })(); + + exports["default"] = XSadd; + + function period_certification(xsadd) { + if (xsadd.state[0] === 0 && xsadd.state[1] === 0 && xsadd.state[2] === 0 && xsadd.state[3] === 0) { + xsadd.state[0] = 88; // X + xsadd.state[1] = 83; // S + xsadd.state[2] = 65; // A + xsadd.state[3] = 68; // D + } + } + + var sh1 = 15; + var sh2 = 18; + var sh3 = 11; + function next_state(xsadd) { + var t = xsadd.state[0]; + t ^= t << sh1; + t ^= t >>> sh2; + t ^= xsadd.state[3] << sh3; + xsadd.state[0] = xsadd.state[1]; + xsadd.state[1] = xsadd.state[2]; + xsadd.state[2] = xsadd.state[3]; + xsadd.state[3] = t; + } + module.exports = exports["default"]; + + +/***/ }, +/* 130 */ +/***/ function(module, exports, __webpack_require__) { + + module.exports = exports = __webpack_require__(131); + exports.kernel = __webpack_require__(132).kernel; + + +/***/ }, +/* 131 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + var kernel = __webpack_require__(132).kernel; + var getKernel = __webpack_require__(132).getKernel; + /** - * Computes the weighted grand mean for a list of means and sample sizes - * @param {Array} means - Mean values for each set of samples - * @param {Array} samples - Number of original values for each set of samples - * @returns {number} + * Parameters to implement function + * @type {{C: number, tol: number, max_passes: number, par: number, k: string}} + * @param {number} C - regularization parameter + * @param {number} tol - numerical tolerance + * @param {number} max_passes - max number of times to iterate over alphas without + * changing + * @param {string} k - the kind of kernel + * @param {number} par - parameter used in the polynomial and the radial function + * of the kernel */ - exports.grandMean = function grandMean(means, samples) { - var sum = 0; - var n = 0; - var l = means.length; - for (var i = 0; i < l; i++) { - sum += samples[i] * means[i]; - n += samples[i]; - } - return sum / n; + var defaultOptions = { + C: 10, + tol: 10e-2, + max_passes: 100, + par: 2, + k: 'lineal' }; /** - * Computes the truncated mean of the given values using a given percentage - * @param {Array} values - * @param {number} percent - The percentage of values to keep (range: [0,1]) - * @param {boolean} [alreadySorted=false] - * @returns {number} - */ - exports.truncatedMean = function truncatedMean(values, percent, alreadySorted) { - if (alreadySorted === undefined) alreadySorted = false; - if (!alreadySorted) { - values = values.slice().sort(compareNumbers); - } - var l = values.length; - var k = Math.floor(l * percent); - var sum = 0; - for (var i = k; i < (l - k); i++) { - sum += values[i]; + * Function to calculate the estimated prediction + * @param {Array } x - point where calculate the function prediction + * @param {Array >} X - training data point in the form (x1, x2) + * @param {Array } Y - training data labels in the domain {1,-1} + * @param {Array } alpha - Lagrange multipliers + * @param {number} b - threshold of the function + * @param {string} k - the kind of kernel + * @param {number} par - parameter used in the polynomial and the radial function + * of the kernel + * @returns {number} + */ + function f(x, X, Y, alpha, b, kernel, par) { + var m = X.length; + var aux = b; + for (var i = 0; i < m; i++) { + b += alpha[i]*Y[i]*kernel(X[i],x, par) } - return sum / (l - 2 * k); - }; + return aux; + } /** - * Computes the harmonic mean of the given values - * @param {Array} values - * @returns {number} + * Simplified version of the Sequential Minimal Optimization algorithm for training + * support vector machines + * @param {{json}} options - parameters to implement function + * @constructor */ - exports.harmonicMean = function harmonicMean(values) { - var sum = 0; - var l = values.length; - for (var i = 0; i < l; i++) { - if (values[i] === 0) { - throw new RangeError('value at index ' + i + 'is zero'); + function SVM(options) { + options = options || {}; + this.options = {}; + for (var o in defaultOptions) { + if (options.hasOwnProperty(o)) { + this.options[o] = options[o]; + } else { + this.options[o] = defaultOptions[o]; } - sum += 1 / values[i]; } - return l / sum; - }; + this.kernel = getKernel(this.options.k); + this.b = 0; + } /** - * Computes the contraharmonic mean of the given values - * @param {Array} values - * @returns {number} + * Train the SVM model + * @param {Array >} X - training data point in the form (x1, x2) + * @param {Array } Y - training data labels in the domain {1,-1} */ - exports.contraHarmonicMean = function contraHarmonicMean(values) { - var r1 = 0; - var r2 = 0; - var l = values.length; - for (var i = 0; i < l; i++) { - r1 += values[i] * values[i]; - r2 += values[i]; + SVM.prototype.train = function (X, Y) { + var m = Y.length; + var alpha = new Array(m); + for (var a = 0; a < m; a++) + alpha[a] = 0; + if (X.length !== m) + throw new TypeError('Arrays should have the same length'); + var b = 0, + b1 = 0, + b2 = 0, + iter = 0, + Ei = 0, + Ej = 0, + ai = 0, + aj = 0, + L = 0, + H = 0, + eta = 0; + + while (iter < this.options.max_passes) { + var numChange = 0; + for (var i = 0; i < m; i++) { + Ei = f(X[i],X,Y,alpha,b,this.kernel,this.options.par) - Y[i]; + if (((Y[i]*Ei < -this.options.tol) && (alpha[i] < this.options.C)) || ((Y[i]*Ei > this.options.tol) && (alpha[i] > 0))) { + var j = 0; + do { + j = Math.ceil(Math.random()*(m - 1)); + } + while (j === i); + Ej = f(X[j],X,Y,alpha,b,this.kernel,this.options.par) - Y[j]; + ai = alpha[i]; + aj = alpha[j]; + if (Y[i] === Y[j]) { + L = Math.max(0, ai+aj-this.options.C); + H = Math.min(this.options.C, ai+aj); + } + else { + L = Math.max(0, ai-aj); + H = Math.min(this.options.C, this.options.C-ai+aj); + } + if (L !== H) { + eta = 2*this.kernel(X[i],X[j], this.options.par) - this.kernel(X[i],X[i], this.options.par) - this.kernel(X[j],X[j], this.options.par); + if (eta < 0) { + alpha[j] = alpha[j] - (Y[j]*(Ei - Ej)) / eta; + if (alpha[j] > H) + alpha[j] = H; + else if (alpha[j] < L) + alpha[j] = L; + if (Math.abs(aj - alpha[j]) >= 10e-5) { + alpha[i] = alpha[i] + Y[i]*Y[j]*(aj - alpha[j]); + b1 = b - Ei - Y[i]*(alpha[i] - ai)*this.kernel(X[i],X[i], this.options.par) - Y[j]*(alpha[j] - aj)*this.kernel(X[i],X[j], this.options.par); + b2 = b - Ej - Y[i]*(alpha[i] - ai)*this.kernel(X[i],X[j], this.options.par) - Y[j]*(alpha[j] - aj)*this.kernel(X[j],X[j], this.options.par); + if ((alpha[i] < this.options.C) && (alpha[i] > 0)) + b = b1; + else if ((alpha[j] < this.options.C) && (alpha[j] > 0)) + b = b2; + else + b = (b1 + b2) / 2; + numChange += 1; + } + } + } + } + } + if (numChange == 0) + iter += 1; + else + iter = 0; } - if (r2 < 0) { - throw new RangeError('sum of values is negative'); + this.b = b; + var s = X[0].length; + this.W = new Array(s); + for (var r = 0; r < s; r++) { + this.W[r] = 0; + for (var w = 0; w < m; w++) + this.W[r] += Y[w]*alpha[w]*X[w][r]; } - return r1 / r2; + this.alphas = alpha.splice(); }; /** - * Computes the median of the given values - * @param {Array} values - * @param {boolean} [alreadySorted=false] - * @returns {number} + * Recreates a SVM based in the exported model + * @param {{name: string, ,options: {json} ,alpha: Array, b: number}} model + * @returns {SVM} */ - exports.median = function median(values, alreadySorted) { - if (alreadySorted === undefined) alreadySorted = false; - if (!alreadySorted) { - values = values.slice().sort(compareNumbers); - } - var l = values.length; - var half = Math.floor(l / 2); - if (l % 2 === 0) { - return (values[half - 1] + values[half]) * 0.5; + SVM.load = function (model) { + if (model.name === 'SVM') { + var svm = new SVM(model.options); + svm.W = model.W.slice(); + svm.b = model.b; + return svm; } else { - return values[half]; + throw new TypeError('expecting a SVM model'); } }; /** - * Computes the variance of the given values - * @param {Array} values - * @param {boolean} [unbiased=true] - if true, divide by (n-1); if false, divide by n. - * @returns {number} + * Let's have a JSON to recreate the model + * @returns {{name: String("SVM"), ,options: {json} ,alpha: Array, b: number}} + * name identifier, options to recreate model, the Lagrange multipliers and the + * threshold of the objective function */ - exports.variance = function variance(values, unbiased) { - if (unbiased === undefined) unbiased = true; - var theMean = exports.mean(values); - var theVariance = 0; - var l = values.length; - - for (var i = 0; i < l; i++) { - var x = values[i] - theMean; - theVariance += x * x; - } - - if (unbiased) { - return theVariance / (l - 1); - } else { - return theVariance / l; - } + SVM.prototype.export = function () { + var model = { + name: 'SVM' + }; + model.options = this.options; + model.W = this.W; + model.b = this.b; + return model; }; /** - * Computes the standard deviation of the given values - * @param {Array} values - * @param {boolean} [unbiased=true] - if true, divide by (n-1); if false, divide by n. - * @returns {number} + * Return the Lagrange multipliers + * @returns {Array } */ - exports.standardDeviation = function standardDeviation(values, unbiased) { - return Math.sqrt(exports.variance(values, unbiased)); + SVM.prototype.getAlphas = function () { + return this.alphas.slice(); }; - exports.standardError = function standardError(values) { - return exports.standardDeviation(values) / Math.sqrt(values.length); + /** + * Returns the threshold of the model function + * @returns {number} threshold of the function + */ + SVM.prototype.getThreshold = function () { + return this.b; }; - exports.quartiles = function quartiles(values, alreadySorted) { - if (typeof(alreadySorted) === 'undefined') alreadySorted = false; - if (!alreadySorted) { - values = values.slice(); - values.sort(compareNumbers); + /** + * Use the train model to make predictions + * @param {Array} p - An array or a single dot to have the prediction + * @returns {*} An array or a single {-1, 1} value of the prediction + */ + SVM.prototype.predict = function (p) { + var ev; + if (Array.isArray(p) && (Array.isArray(p[0]) || (typeof p[0] === 'object'))) { + var ans = new Array(p.length); + for (var i = 0; i < ans.length; i++) { + ev = this.b; + for (var j = 0; j < this.W.length; j++) + ev += this.W[j]*p[j]; + if (ev < 0) + ans[i] = -1; + else + ans[i] = 1; + } + return ans; + } + else { + ev = this.b; + for (var e = 0; e < this.W.length; e++) + ev += this.W[e]*p[e]; + if (ev < 0) + return -1; + else + return 1; } + }; - var quart = values.length / 4; - var q1 = values[Math.ceil(quart) - 1]; - var q2 = exports.median(values, true); - var q3 = values[Math.ceil(quart * 3) - 1]; + module.exports = SVM; - return {q1: q1, q2: q2, q3: q3}; - }; +/***/ }, +/* 132 */ +/***/ function(module, exports) { + + 'use strict'; + + /** + * Kernel function to return the dot product for different spaces + * @param {Array } x1 - input first vector + * @param {Array } x2 - input second vector + * @param {string} func - the kind of transformation + * @param {number} par - parameter used in the polynomial and the radial function + * @return {number} calculus of the dot product using the function + * */ + function kernel(x1,x2,func,par) { + return getKernel(func)(x1, x2, par); + } - exports.pooledStandardDeviation = function pooledStandardDeviation(samples, unbiased) { - return Math.sqrt(exports.pooledVariance(samples, unbiased)); - }; + /** + * The dot product between the p1 and p2 vectors + * @param {Array } p1 - first vector to get dot product + * @param {Array } p2 - second vector to get dot product + * @returns {number} dot product between the p1 and p2 vectors + */ + function dot(p1, p2) { + var l = p1.length; + var prod = 0; - exports.pooledVariance = function pooledVariance(samples, unbiased) { - if (typeof(unbiased) === 'undefined') unbiased = true; - var sum = 0; - var length = 0, l = samples.length; for (var i = 0; i < l; i++) { - var values = samples[i]; - var vari = exports.variance(values); + prod += p1[i] * p2[i]; + } - sum += (values.length - 1) * vari; + return prod; + } - if (unbiased) - length += values.length - 1; - else - length += values.length; - } - return sum / length; - }; + function getKernel(func) { + func = (typeof func === 'undefined') ? 'lineal' : func; - exports.mode = function mode(values) { - var l = values.length, - itemCount = new Array(l), - i; - for (i = 0; i < l; i++) { - itemCount[i] = 0; + switch(func) { + case 'lineal': + return kernelLineal; + case 'polynomial': + return kernelPolynomial; + case 'radial': + return kernelRadial; + default: + throw new TypeError('Function kernel undefined: ' + func); } - var itemArray = new Array(l); - var count = 0; + } - for (i = 0; i < l; i++) { - var index = itemArray.indexOf(values[i]); - if (index >= 0) - itemCount[index]++; - else { - itemArray[count] = values[i]; - itemCount[count] = 1; - count++; - } - } + function kernelLineal(x1,x2) { + return dot(x1,x2); + } - var maxValue = 0, maxIndex = 0; - for (i = 0; i < count; i++) { - if (itemCount[i] > maxValue) { - maxValue = itemCount[i]; - maxIndex = i; - } + function kernelPolynomial(x1, x2, par) { + par = (typeof par === 'undefined') ? 2 : par; + return Math.pow((dot(x1, x2) + 1), par); + } + + function kernelRadial(x1, x2, par) { + par = (typeof par === 'undefined') ? 2 : par; + var l = x1.length; + var rest = new Array(l); + for (var i = 0; i < l; i++) { + rest[i] = x1[i] - x2[i]; } + var norm = dot(rest, rest); + return Math.exp((norm)/(-2*par*par)); + } - return itemArray[maxIndex]; + module.exports = { + kernel: kernel, + getKernel: getKernel, + lineal : kernelLineal, + polynomial : kernelPolynomial, + radial : kernelRadial }; - exports.covariance = function covariance(vector1, vector2, unbiased) { - if (typeof(unbiased) === 'undefined') unbiased = true; - var mean1 = exports.mean(vector1); - var mean2 = exports.mean(vector2); - if (vector1.length !== vector2.length) - throw "Vectors do not have the same dimensions"; +/***/ }, +/* 133 */ +/***/ function(module, exports, __webpack_require__) { - var cov = 0, l = vector1.length; - for (var i = 0; i < l; i++) { - var x = vector1[i] - mean1; - var y = vector2[i] - mean2; - cov += x * y; - } + 'use strict'; - if (unbiased) - return cov / (l - 1); - else - return cov / l; - }; + module.exports = __webpack_require__(134); - exports.skewness = function skewness(values, unbiased) { - if (typeof(unbiased) === 'undefined') unbiased = true; - var theMean = exports.mean(values); +/***/ }, +/* 134 */ +/***/ function(module, exports, __webpack_require__) { - var s2 = 0, s3 = 0, l = values.length; - for (var i = 0; i < l; i++) { - var dev = values[i] - theMean; - s2 += dev * dev; - s3 += dev * dev * dev; - } - var m2 = s2 / l; - var m3 = s3 / l; + 'use strict'; - var g = m3 / (Math.pow(m2, 3 / 2.0)); - if (unbiased) { - var a = Math.sqrt(l * (l - 1)); - var b = l - 2; - return (a / b) * g; - } - else { - return g; + module.exports = KNN; + + var KDTree = __webpack_require__(135).kdTree; + var Distances = __webpack_require__(38); + + /** + * K-Nearest neighboor constructor. + * + * @param reload - loading purposes. + * @param model - loading purposes + * @constructor + */ + function KNN(reload, model) { + if(reload) { + this.kdtree = model.kdtree; + this.k = model.k; + this.classes = model.classes; } - }; + } - exports.kurtosis = function kurtosis(values, unbiased) { - if (typeof(unbiased) === 'undefined') unbiased = true; - var theMean = exports.mean(values); - var n = values.length, s2 = 0, s4 = 0; + /** + * Function that trains the KNN with the given trainingSet and trainingLabels. + * The third argument is an object with the following options. + * * distance: that represent the distance function applied (default: euclidean) + * * k: the number of neighboors to take in count for classify (default: number of features + 1) + * + * @param trainingSet + * @param trainingLabels + * @param options + */ + KNN.prototype.train = function (trainingSet, trainingLabels, options) { + if(options === undefined) options = {}; + if(options.distance === undefined) options.distance = Distances.distance.euclidean; + if(options.k === undefined) options.k = trainingSet[0].length + 1; - for (var i = 0; i < n; i++) { - var dev = values[i] - theMean; - s2 += dev * dev; - s4 += dev * dev * dev * dev; + var classes = 0; + var exist = new Array(1000); + var j = 0; + for(var i = 0; i < trainingLabels.length; ++i) { + if(exist.indexOf(trainingLabels[i]) === -1) { + classes++; + exist[j] = trainingLabels[i]; + j++; + } } - var m2 = s2 / n; - var m4 = s4 / n; - if (unbiased) { - var v = s2 / (n - 1); - var a = (n * (n + 1)) / ((n - 1) * (n - 2) * (n - 3)); - var b = s4 / (v * v); - var c = ((n - 1) * (n - 1)) / ((n - 2) * (n - 3)); + // copy dataset + var points = new Array(trainingSet.length); + for(i = 0; i < points.length; ++i) { + points[i] = trainingSet[i].slice(); + } - return a * b - 3 * c; + this.features = trainingSet[0].length; + for(i = 0; i < trainingLabels.length; ++i) { + points[i].push(trainingLabels[i]); } - else { - return m4 / (m2 * m2) - 3; + + var dimensions = new Array(trainingSet[0].length); + for(i = 0; i < dimensions.length; ++i) { + dimensions[i] = i; } - }; - exports.entropy = function entropy(values, eps) { - if (typeof(eps) === 'undefined') eps = 0; - var sum = 0, l = values.length; - for (var i = 0; i < l; i++) - sum += values[i] * Math.log(values[i] + eps); - return -sum; + this.kdtree = new KDTree(points, options.distance, dimensions); + this.k = options.k; + this.classes = classes; }; - exports.weightedMean = function weightedMean(values, weights) { - var sum = 0, l = values.length; - for (var i = 0; i < l; i++) - sum += values[i] * weights[i]; - return sum; - }; + /** + * Function that returns the predictions given the dataset. + * + * @param dataset + * @returns {Array} + */ + KNN.prototype.predict = function (dataset) { + var predictions = new Array(dataset.length); + for(var i = 0; i < dataset.length; ++i) { + predictions[i] = this.getSinglePrediction(dataset[i]); + } - exports.weightedStandardDeviation = function weightedStandardDeviation(values, weights) { - return Math.sqrt(exports.weightedVariance(values, weights)); + return predictions; }; - exports.weightedVariance = function weightedVariance(values, weights) { - var theMean = exports.weightedMean(values, weights); - var vari = 0, l = values.length; - var a = 0, b = 0; + /** + * function that returns a prediction for a single case. + * @param currentCase + * @returns {number} + */ + KNN.prototype.getSinglePrediction = function (currentCase) { + var nearestPoints = this.kdtree.nearest(currentCase, this.k); + var pointsPerClass = new Array(this.classes); + var predictedClass = -1; + var maxPoints = -1; + var lastElement = nearestPoints[0][0].length - 1; - for (var i = 0; i < l; i++) { - var z = values[i] - theMean; - var w = weights[i]; + for(var i = 0; i < pointsPerClass.length; ++i) { + pointsPerClass[i] = 0; + } - vari += w * (z * z); - b += w; - a += w * w; + for(i = 0; i < nearestPoints.length; ++i) { + var currentClass = nearestPoints[i][0][lastElement]; + var currentPoints = ++pointsPerClass[currentClass]; + if(currentPoints > maxPoints) { + predictedClass = currentClass; + maxPoints = currentPoints; + } } - return vari * (b / (b * b - a)); + return predictedClass; }; - exports.center = function center(values, inPlace) { - if (typeof(inPlace) === 'undefined') inPlace = false; - - var result = values; - if (!inPlace) - result = values.slice(); - - var theMean = exports.mean(result), l = result.length; - for (var i = 0; i < l; i++) - result[i] -= theMean; - }; + /** + * function that returns a KNN classifier with the given model. + * + * @param model + */ + KNN.load = function (model) { + if(model.modelName !== "KNN") + throw new RangeError("The given model is invalid!"); - exports.standardize = function standardize(values, standardDev, inPlace) { - if (typeof(standardDev) === 'undefined') standardDev = exports.standardDeviation(values); - if (typeof(inPlace) === 'undefined') inPlace = false; - var l = values.length; - var result = inPlace ? values : new Array(l); - for (var i = 0; i < l; i++) - result[i] = values[i] / standardDev; - return result; + return new KNN(true, model); }; - exports.cumulativeSum = function cumulativeSum(array) { - var l = array.length; - var result = new Array(l); - result[0] = array[0]; - for (var i = 1; i < l; i++) - result[i] = result[i - 1] + array[i]; - return result; + /** + * function that exports the current KNN classifier. + */ + KNN.prototype.export = function () { + return { + modelName: "KNN", + kdtree: this.kdtree, + k: this.k, + classes: this.classes + }; }; - /***/ }, -/* 118 */ -/***/ function(module, exports, __webpack_require__) { +/* 135 */ +/***/ function(module, exports) { 'use strict'; - var arrayStat = __webpack_require__(117); - // https://github.com/accord-net/framework/blob/development/Sources/Accord.Statistics/Tools.cs + /** + * k-d Tree JavaScript - V 1.01 + * + * https://github.com/ubilabs/kd-tree-javascript + * + * @author Mircea Pricop , 2012 + * @author Martin Kleppe , 2012 + * @author Ubilabs http://ubilabs.net, 2012 + * @license MIT License + */ - function entropy(matrix, eps) { - if (typeof(eps) === 'undefined') { - eps = 0; - } - var sum = 0, - l1 = matrix.length, - l2 = matrix[0].length; - for (var i = 0; i < l1; i++) { - for (var j = 0; j < l2; j++) { - sum += matrix[i][j] * Math.log(matrix[i][j] + eps); - } - } - return -sum; + + function Node(obj, dimension, parent) { + this.obj = obj; + this.left = null; + this.right = null; + this.parent = parent; + this.dimension = dimension; } - function mean(matrix, dimension) { - if (typeof(dimension) === 'undefined') { - dimension = 0; - } - var rows = matrix.length, - cols = matrix[0].length, - theMean, N, i, j; + function kdTree(points, metric, dimensions) { - if (dimension === -1) { - theMean = [0]; - N = rows * cols; - for (i = 0; i < rows; i++) { - for (j = 0; j < cols; j++) { - theMean[0] += matrix[i][j]; - } - } - theMean[0] /= N; - } else if (dimension === 0) { - theMean = new Array(cols); - N = rows; - for (j = 0; j < cols; j++) { - theMean[j] = 0; - for (i = 0; i < rows; i++) { - theMean[j] += matrix[i][j]; - } - theMean[j] /= N; + var self = this; + + function buildTree(points, depth, parent) { + var dim = depth % dimensions.length, + median, + node; + + if (points.length === 0) { + return null; } - } else if (dimension === 1) { - theMean = new Array(rows); - N = cols; - for (j = 0; j < rows; j++) { - theMean[j] = 0; - for (i = 0; i < cols; i++) { - theMean[j] += matrix[j][i]; - } - theMean[j] /= N; + if (points.length === 1) { + return new Node(points[0], dim, parent); } - } else { - throw new Error('Invalid dimension'); - } - return theMean; - } - function standardDeviation(matrix, means, unbiased) { - var vari = variance(matrix, means, unbiased), l = vari.length; - for (var i = 0; i < l; i++) { - vari[i] = Math.sqrt(vari[i]); - } - return vari; - } + points.sort(function (a, b) { + return a[dimensions[dim]] - b[dimensions[dim]]; + }); - function variance(matrix, means, unbiased) { - if (typeof(unbiased) === 'undefined') { - unbiased = true; - } - means = means || mean(matrix); - var rows = matrix.length; - if (rows === 0) return []; - var cols = matrix[0].length; - var vari = new Array(cols); + median = Math.floor(points.length / 2); + node = new Node(points[median], dim, parent); + node.left = buildTree(points.slice(0, median), depth + 1, node); + node.right = buildTree(points.slice(median + 1), depth + 1, node); - for (var j = 0; j < cols; j++) { - var sum1 = 0, sum2 = 0, x = 0; - for (var i = 0; i < rows; i++) { - x = matrix[i][j] - means[j]; - sum1 += x; - sum2 += x * x; - } - if (unbiased) { - vari[j] = (sum2 - ((sum1 * sum1) / rows)) / (rows - 1); - } else { - vari[j] = (sum2 - ((sum1 * sum1) / rows)) / rows; - } + return node; } - return vari; - } - function median(matrix) { - var rows = matrix.length, cols = matrix[0].length; - var medians = new Array(cols); + // Reloads a serialied tree + function loadTree (data) { + // Just need to restore the `parent` parameter + self.root = data; - for (var i = 0; i < cols; i++) { - var data = new Array(rows); - for (var j = 0; j < rows; j++) { - data[j] = matrix[j][i]; - } - data.sort(); - var N = data.length; - if (N % 2 === 0) { - medians[i] = (data[N / 2] + data[(N / 2) - 1]) * 0.5; - } else { - medians[i] = data[Math.floor(N / 2)]; + function restoreParent (root) { + if (root.left) { + root.left.parent = root; + restoreParent(root.left); + } + + if (root.right) { + root.right.parent = root; + restoreParent(root.right); + } } + + restoreParent(self.root); } - return medians; - } - function mode(matrix) { - var rows = matrix.length, - cols = matrix[0].length, - modes = new Array(cols), - i, j; - for (i = 0; i < cols; i++) { - var itemCount = new Array(rows); - for (var k = 0; k < rows; k++) { - itemCount[k] = 0; - } - var itemArray = new Array(rows); - var count = 0; + // If points is not an array, assume we're loading a pre-built tree + if (!Array.isArray(points)) loadTree(points, metric, dimensions); + else this.root = buildTree(points, 0, null); - for (j = 0; j < rows; j++) { - var index = itemArray.indexOf(matrix[j][i]); - if (index >= 0) { - itemCount[index]++; - } else { - itemArray[count] = matrix[j][i]; - itemCount[count] = 1; - count++; + // Convert to a JSON serializable structure; this just requires removing + // the `parent` property + this.toJSON = function (src) { + if (!src) src = this.root; + var dest = new Node(src.obj, src.dimension, null); + if (src.left) dest.left = self.toJSON(src.left); + if (src.right) dest.right = self.toJSON(src.right); + return dest; + }; + + this.insert = function (point) { + function innerSearch(node, parent) { + + if (node === null) { + return parent; } - } - var maxValue = 0, maxIndex = 0; - for (j = 0; j < count; j++) { - if (itemCount[j] > maxValue) { - maxValue = itemCount[j]; - maxIndex = j; + var dimension = dimensions[node.dimension]; + if (point[dimension] < node.obj[dimension]) { + return innerSearch(node.left, node); + } else { + return innerSearch(node.right, node); } } - modes[i] = itemArray[maxIndex]; - } - return modes; - } - - function skewness(matrix, unbiased) { - if (typeof(unbiased) === 'undefined') unbiased = true; - var means = mean(matrix); - var n = matrix.length, l = means.length; - var skew = new Array(l); + var insertPosition = innerSearch(this.root, null), + newNode, + dimension; - for (var j = 0; j < l; j++) { - var s2 = 0, s3 = 0; - for (var i = 0; i < n; i++) { - var dev = matrix[i][j] - means[j]; - s2 += dev * dev; - s3 += dev * dev * dev; + if (insertPosition === null) { + this.root = new Node(point, 0, null); + return; } - var m2 = s2 / n; - var m3 = s3 / n; - var g = m3 / Math.pow(m2, 3 / 2); + newNode = new Node(point, (insertPosition.dimension + 1) % dimensions.length, insertPosition); + dimension = dimensions[insertPosition.dimension]; - if (unbiased) { - var a = Math.sqrt(n * (n - 1)); - var b = n - 2; - skew[j] = (a / b) * g; + if (point[dimension] < insertPosition.obj[dimension]) { + insertPosition.left = newNode; } else { - skew[j] = g; + insertPosition.right = newNode; } - } - return skew; - } + }; - function kurtosis(matrix, unbiased) { - if (typeof(unbiased) === 'undefined') unbiased = true; - var means = mean(matrix); - var n = matrix.length, m = matrix[0].length; - var kurt = new Array(m); + this.remove = function (point) { + var node; - for (var j = 0; j < m; j++) { - var s2 = 0, s4 = 0; - for (var i = 0; i < n; i++) { - var dev = matrix[i][j] - means[j]; - s2 += dev * dev; - s4 += dev * dev * dev * dev; - } - var m2 = s2 / n; - var m4 = s4 / n; + function nodeSearch(node) { + if (node === null) { + return null; + } - if (unbiased) { - var v = s2 / (n - 1); - var a = (n * (n + 1)) / ((n - 1) * (n - 2) * (n - 3)); - var b = s4 / (v * v); - var c = ((n - 1) * (n - 1)) / ((n - 2) * (n - 3)); - kurt[j] = a * b - 3 * c; - } else { - kurt[j] = m4 / (m2 * m2) - 3; + if (node.obj === point) { + return node; + } + + var dimension = dimensions[node.dimension]; + + if (point[dimension] < node.obj[dimension]) { + return nodeSearch(node.left, node); + } else { + return nodeSearch(node.right, node); + } } - } - return kurt; - } - function standardError(matrix) { - var samples = matrix.length; - var standardDeviations = standardDeviation(matrix), l = standardDeviations.length; - var standardErrors = new Array(l); - var sqrtN = Math.sqrt(samples); + function removeNode(node) { + var nextNode, + nextObj, + pDimension; - for (var i = 0; i < l; i++) { - standardErrors[i] = standardDeviations[i] / sqrtN; - } - return standardErrors; - } + function findMin(node, dim) { + var dimension, + own, + left, + right, + min; - function covariance(matrix, dimension) { - return scatter(matrix, undefined, dimension); - } + if (node === null) { + return null; + } - function scatter(matrix, divisor, dimension) { - if (typeof(dimension) === 'undefined') { - dimension = 0; - } - if (typeof(divisor) === 'undefined') { - if (dimension === 0) { - divisor = matrix.length - 1; - } else if (dimension === 1) { - divisor = matrix[0].length - 1; - } - } - var means = mean(matrix, dimension), - rows = matrix.length; - if (rows === 0) { - return [[]]; - } - var cols = matrix[0].length, - cov, i, j, s, k; + dimension = dimensions[dim]; - if (dimension === 0) { - cov = new Array(cols); - for (i = 0; i < cols; i++) { - cov[i] = new Array(cols); - } - for (i = 0; i < cols; i++) { - for (j = i; j < cols; j++) { - s = 0; - for (k = 0; k < rows; k++) { - s += (matrix[k][j] - means[j]) * (matrix[k][i] - means[i]); + if (node.dimension === dim) { + if (node.left !== null) { + return findMin(node.left, dim); + } + return node; } - s /= divisor; - cov[i][j] = s; - cov[j][i] = s; - } - } - } else if (dimension === 1) { - cov = new Array(rows); - for (i = 0; i < rows; i++) { - cov[i] = new Array(rows); - } - for (i = 0; i < rows; i++) { - for (j = i; j < rows; j++) { - s = 0; - for (k = 0; k < cols; k++) { - s += (matrix[j][k] - means[j]) * (matrix[i][k] - means[i]); + + own = node.obj[dimension]; + left = findMin(node.left, dim); + right = findMin(node.right, dim); + min = node; + + if (left !== null && left.obj[dimension] < own) { + min = left; } - s /= divisor; - cov[i][j] = s; - cov[j][i] = s; + if (right !== null && right.obj[dimension] < min.obj[dimension]) { + min = right; + } + return min; } - } - } else { - throw new Error('Invalid dimension'); - } - return cov; - } + if (node.left === null && node.right === null) { + if (node.parent === null) { + self.root = null; + return; + } - function correlation(matrix) { - var means = mean(matrix), - standardDeviations = standardDeviation(matrix, true, means), - scores = zScores(matrix, means, standardDeviations), - rows = matrix.length, - cols = matrix[0].length, - i, j; + pDimension = dimensions[node.parent.dimension]; - var cor = new Array(cols); - for (i = 0; i < cols; i++) { - cor[i] = new Array(cols); - } - for (i = 0; i < cols; i++) { - for (j = i; j < cols; j++) { - var c = 0; - for (var k = 0, l = scores.length; k < l; k++) { - c += scores[k][j] * scores[k][i]; + if (node.obj[pDimension] < node.parent.obj[pDimension]) { + node.parent.left = null; + } else { + node.parent.right = null; + } + return; } - c /= rows - 1; - cor[i][j] = c; - cor[j][i] = c; - } - } - return cor; - } - - function zScores(matrix, means, standardDeviations) { - means = means || mean(matrix); - if (typeof(standardDeviations) === 'undefined') standardDeviations = standardDeviation(matrix, true, means); - return standardize(center(matrix, means, false), standardDeviations, true); - } - function center(matrix, means, inPlace) { - means = means || mean(matrix); - var result = matrix, - l = matrix.length, - i, j, jj; + // If the right subtree is not empty, swap with the minimum element on the + // node's dimension. If it is empty, we swap the left and right subtrees and + // do the same. + if (node.right !== null) { + nextNode = findMin(node.right, node.dimension); + nextObj = nextNode.obj; + removeNode(nextNode); + node.obj = nextObj; + } else { + nextNode = findMin(node.left, node.dimension); + nextObj = nextNode.obj; + removeNode(nextNode); + node.right = node.left; + node.left = null; + node.obj = nextObj; + } - if (!inPlace) { - result = new Array(l); - for (i = 0; i < l; i++) { - result[i] = new Array(matrix[i].length); } - } - for (i = 0; i < l; i++) { - var row = result[i]; - for (j = 0, jj = row.length; j < jj; j++) { - row[j] = matrix[i][j] - means[j]; - } - } - return result; - } + node = nodeSearch(self.root); - function standardize(matrix, standardDeviations, inPlace) { - if (typeof(standardDeviations) === 'undefined') standardDeviations = standardDeviation(matrix); - var result = matrix, - l = matrix.length, - i, j, jj; + if (node === null) { return; } - if (!inPlace) { - result = new Array(l); - for (i = 0; i < l; i++) { - result[i] = new Array(matrix[i].length); - } - } + removeNode(node); + }; - for (i = 0; i < l; i++) { - var resultRow = result[i]; - var sourceRow = matrix[i]; - for (j = 0, jj = resultRow.length; j < jj; j++) { - if (standardDeviations[j] !== 0 && !isNaN(standardDeviations[j])) { - resultRow[j] = sourceRow[j] / standardDeviations[j]; - } - } - } - return result; - } + this.nearest = function (point, maxNodes, maxDistance) { + var i, + result, + bestNodes; - function weightedVariance(matrix, weights) { - var means = mean(matrix); - var rows = matrix.length; - if (rows === 0) return []; - var cols = matrix[0].length; - var vari = new Array(cols); + bestNodes = new BinaryHeap( + function (e) { return -e[1]; } + ); - for (var j = 0; j < cols; j++) { - var sum = 0; - var a = 0, b = 0; + function nearestSearch(node) { + var bestChild, + dimension = dimensions[node.dimension], + ownDistance = metric(point, node.obj), + linearPoint = {}, + linearDistance, + otherChild, + i; - for (var i = 0; i < rows; i++) { - var z = matrix[i][j] - means[j]; - var w = weights[i]; + function saveNode(node, distance) { + bestNodes.push([node, distance]); + if (bestNodes.size() > maxNodes) { + bestNodes.pop(); + } + } - sum += w * (z * z); - b += w; - a += w * w; - } + for (i = 0; i < dimensions.length; i += 1) { + if (i === node.dimension) { + linearPoint[dimensions[i]] = point[dimensions[i]]; + } else { + linearPoint[dimensions[i]] = node.obj[dimensions[i]]; + } + } - vari[j] = sum * (b / (b * b - a)); - } + linearDistance = metric(linearPoint, node.obj); - return vari; - } + if (node.right === null && node.left === null) { + if (bestNodes.size() < maxNodes || ownDistance < bestNodes.peek()[1]) { + saveNode(node, ownDistance); + } + return; + } - function weightedMean(matrix, weights, dimension) { - if (typeof(dimension) === 'undefined') { - dimension = 0; - } - var rows = matrix.length; - if (rows === 0) return []; - var cols = matrix[0].length, - means, i, ii, j, w, row; + if (node.right === null) { + bestChild = node.left; + } else if (node.left === null) { + bestChild = node.right; + } else { + if (point[dimension] < node.obj[dimension]) { + bestChild = node.left; + } else { + bestChild = node.right; + } + } - if (dimension === 0) { - means = new Array(cols); - for (i = 0; i < cols; i++) { - means[i] = 0; + nearestSearch(bestChild); + + if (bestNodes.size() < maxNodes || ownDistance < bestNodes.peek()[1]) { + saveNode(node, ownDistance); + } + + if (bestNodes.size() < maxNodes || Math.abs(linearDistance) < bestNodes.peek()[1]) { + if (bestChild === node.left) { + otherChild = node.right; + } else { + otherChild = node.left; + } + if (otherChild !== null) { + nearestSearch(otherChild); + } + } } - for (i = 0; i < rows; i++) { - row = matrix[i]; - w = weights[i]; - for (j = 0; j < cols; j++) { - means[j] += row[j] * w; + + if (maxDistance) { + for (i = 0; i < maxNodes; i += 1) { + bestNodes.push([null, maxDistance]); } } - } else if (dimension === 1) { - means = new Array(rows); - for (i = 0; i < rows; i++) { - means[i] = 0; + + if(self.root) + nearestSearch(self.root); + + result = []; + + for (i = 0; i < Math.min(maxNodes, bestNodes.content.length); i += 1) { + if (bestNodes.content[i][0]) { + result.push([bestNodes.content[i][0].obj, bestNodes.content[i][1]]); + } } - for (j = 0; j < rows; j++) { - row = matrix[j]; - w = weights[j]; - for (i = 0; i < cols; i++) { - means[j] += row[i] * w; + return result; + }; + + this.balanceFactor = function () { + function height(node) { + if (node === null) { + return 0; } + return Math.max(height(node.left), height(node.right)) + 1; } - } else { - throw new Error('Invalid dimension'); - } - var weightSum = arrayStat.sum(weights); - if (weightSum !== 0) { - for (i = 0, ii = means.length; i < ii; i++) { - means[i] /= weightSum; + function count(node) { + if (node === null) { + return 0; + } + return count(node.left) + count(node.right) + 1; } - } - return means; + + return height(self.root) / (Math.log(count(self.root)) / Math.log(2)); + }; } - function weightedCovariance(matrix, weights, means, dimension) { - dimension = dimension || 0; - means = means || weightedMean(matrix, weights, dimension); - var s1 = 0, s2 = 0; - for (var i = 0, ii = weights.length; i < ii; i++) { - s1 += weights[i]; - s2 += weights[i] * weights[i]; - } - var factor = s1 / (s1 * s1 - s2); - return weightedScatter(matrix, weights, means, factor, dimension); + // Binary heap implementation from: + // http://eloquentjavascript.net/appendix2.html + + function BinaryHeap(scoreFunction){ + this.content = []; + this.scoreFunction = scoreFunction; } - function weightedScatter(matrix, weights, means, factor, dimension) { - dimension = dimension || 0; - means = means || weightedMean(matrix, weights, dimension); - if (typeof(factor) === 'undefined') { - factor = 1; - } - var rows = matrix.length; - if (rows === 0) { - return [[]]; - } - var cols = matrix[0].length, - cov, i, j, k, s; + BinaryHeap.prototype = { + push: function(element) { + // Add the new element to the end of the array. + this.content.push(element); + // Allow it to bubble up. + this.bubbleUp(this.content.length - 1); + }, - if (dimension === 0) { - cov = new Array(cols); - for (i = 0; i < cols; i++) { - cov[i] = new Array(cols); + pop: function() { + // Store the first element so we can return it later. + var result = this.content[0]; + // Get the element at the end of the array. + var end = this.content.pop(); + // If there are any elements left, put the end element at the + // start, and let it sink down. + if (this.content.length > 0) { + this.content[0] = end; + this.sinkDown(0); } - for (i = 0; i < cols; i++) { - for (j = i; j < cols; j++) { - s = 0; - for (k = 0; k < rows; k++) { - s += weights[k] * (matrix[k][j] - means[j]) * (matrix[k][i] - means[i]); + return result; + }, + + peek: function() { + return this.content[0]; + }, + + remove: function(node) { + var len = this.content.length; + // To remove a value, we must search through the array to find + // it. + for (var i = 0; i < len; i++) { + if (this.content[i] == node) { + // When it is found, the process seen in 'pop' is repeated + // to fill up the hole. + var end = this.content.pop(); + if (i != len - 1) { + this.content[i] = end; + if (this.scoreFunction(end) < this.scoreFunction(node)) + this.bubbleUp(i); + else + this.sinkDown(i); } - cov[i][j] = s * factor; - cov[j][i] = s * factor; + return; } } - } else if (dimension === 1) { - cov = new Array(rows); - for (i = 0; i < rows; i++) { - cov[i] = new Array(rows); + throw new Error("Node not found."); + }, + + size: function() { + return this.content.length; + }, + + bubbleUp: function(n) { + // Fetch the element that has to be moved. + var element = this.content[n]; + // When at 0, an element can not go up any further. + while (n > 0) { + // Compute the parent element's index, and fetch it. + var parentN = Math.floor((n + 1) / 2) - 1, + parent = this.content[parentN]; + // Swap the elements if the parent is greater. + if (this.scoreFunction(element) < this.scoreFunction(parent)) { + this.content[parentN] = element; + this.content[n] = parent; + // Update 'n' to continue at the new position. + n = parentN; + } + // Found a parent that is less, no need to move it further. + else { + break; + } } - for (i = 0; i < rows; i++) { - for (j = i; j < rows; j++) { - s = 0; - for (k = 0; k < cols; k++) { - s += weights[k] * (matrix[j][k] - means[j]) * (matrix[i][k] - means[i]); + }, + + sinkDown: function(n) { + // Look up the target element and its score. + var length = this.content.length, + element = this.content[n], + elemScore = this.scoreFunction(element); + + while(true) { + // Compute the indices of the child elements. + var child2N = (n + 1) * 2, child1N = child2N - 1; + // This is used to store the new position of the element, + // if any. + var swap = null; + // If the first child exists (is inside the array)... + if (child1N < length) { + // Look it up and compute its score. + var child1 = this.content[child1N], + child1Score = this.scoreFunction(child1); + // If the score is less than our element's, we need to swap. + if (child1Score < elemScore) + swap = child1N; + } + // Do the same checks for the other child. + if (child2N < length) { + var child2 = this.content[child2N], + child2Score = this.scoreFunction(child2); + if (child2Score < (swap == null ? elemScore : child1Score)){ + swap = child2N; } - cov[i][j] = s * factor; - cov[j][i] = s * factor; + } + + // If the element needs to be moved, swap it, and continue. + if (swap != null) { + this.content[n] = this.content[swap]; + this.content[swap] = element; + n = swap; + } + // Otherwise, we are done. + else { + break; } } - } else { - throw new Error('Invalid dimension'); } + }; - return cov; - } + this.kdTree = kdTree; - module.exports = { - entropy: entropy, - mean: mean, - standardDeviation: standardDeviation, - variance: variance, - median: median, - mode: mode, - skewness: skewness, - kurtosis: kurtosis, - standardError: standardError, - covariance: covariance, - scatter: scatter, - correlation: correlation, - zScores: zScores, - center: center, - standardize: standardize, - weightedVariance: weightedVariance, - weightedMean: weightedMean, - weightedCovariance: weightedCovariance, - weightedScatter: weightedScatter - }; + exports.kdTree = kdTree; + exports.BinaryHeap = BinaryHeap; /***/ }, -/* 119 */ +/* 136 */ /***/ function(module, exports, __webpack_require__) { - module.exports = exports = __webpack_require__(120); - exports.Utils = __webpack_require__(121); - exports.OPLS = __webpack_require__(125); + 'use strict'; + + module.exports = exports = __webpack_require__(137).NaiveBayes; + exports.separateClasses = __webpack_require__(137).separateClasses; /***/ }, -/* 120 */ +/* 137 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; - module.exports = PLS; var Matrix = __webpack_require__(14); - var Utils = __webpack_require__(121); - - /** - * Retrieves the sum at the column of the given matrix. - * @param matrix - * @param column - * @returns {number} - */ - function getColSum(matrix, column) { - var sum = 0; - for (var i = 0; i < matrix.rows; i++) { - sum += matrix[i][column]; - } - return sum; - } + var Stat = __webpack_require__(138); - /** - * Function that returns the index where the sum of each - * column vector is maximum. - * @param {Matrix} data - * @returns {number} index of the maximum - */ - function maxSumColIndex(data) { - var maxIndex = 0; - var maxSum = -Infinity; - for(var i = 0; i < data.columns; ++i) { - var currentSum = getColSum(data, i); - if(currentSum > maxSum) { - maxSum = currentSum; - maxIndex = i; - } - } - return maxIndex; - } + module.exports.NaiveBayes = NaiveBayes; + module.exports.separateClasses = separateClasses; /** - * Constructor of the PLS model. - * @param reload - used for load purposes. - * @param model - used for load purposes. + * Constructor for the Naive Bayes classifier, the parameters here is just for loading purposes. + * + * @param reload + * @param model * @constructor */ - function PLS(reload, model) { + function NaiveBayes(reload, model) { if(reload) { - this.E = Matrix.checkMatrix(model.E); - this.F = Matrix.checkMatrix(model.F); - this.ssqYcal = model.ssqYcal; - this.R2X = model.R2X; - this.ymean = Matrix.checkMatrix(model.ymean); - this.ystd = Matrix.checkMatrix(model.ystd); - this.PBQ = Matrix.checkMatrix(model.PBQ); - this.T = Matrix.checkMatrix(model.T); - this.P = Matrix.checkMatrix(model.P); - this.U = Matrix.checkMatrix(model.U); - this.Q = Matrix.checkMatrix(model.Q); - this.W = Matrix.checkMatrix(model.W); - this.B = Matrix.checkMatrix(model.B); + this.means = model.means; + this.calculateProbabilities = model.calculateProbabilities; } } /** - * Function that fit the model with the given data and predictions, in this function is calculated the - * following outputs: - * - * T - Score matrix of X - * P - Loading matrix of X - * U - Score matrix of Y - * Q - Loading matrix of Y - * B - Matrix of regression coefficient - * W - Weight matrix of X + * Function that trains the classifier with a matrix that represents the training set and an array that + * represents the label of each row in the training set. the labels must be numbers between 0 to n-1 where + * n represents the number of classes. * - * @param {Matrix} trainingSet - Dataset to be apply the model - * @param {Matrix} predictions - Predictions over each case of the dataset - * @param {Number} options - recieves the latentVectors and the tolerance of each step of the PLS + * WARNING: in the case that one class, all the cases in one or more features have the same value, the + * Naive Bayes classifier will not work well. + * @param trainingSet + * @param trainingLabels */ - PLS.prototype.train = function (trainingSet, predictions, options) { - - if(options === undefined) options = {}; - - var latentVectors = options.latentVectors; - if(latentVectors === undefined || isNaN(latentVectors)) { - throw new RangeError("Latent vector must be a number."); - } - - var tolerance = options.tolerance; - if(tolerance === undefined || isNaN(tolerance)) { - throw new RangeError("Tolerance must be a number"); - } + NaiveBayes.prototype.train = function (trainingSet, trainingLabels) { + var C1 = Math.sqrt(2*Math.PI); // constant to precalculate the squared root + if(!Matrix.isMatrix(trainingSet)) trainingSet = new Matrix(trainingSet); + else trainingSet = trainingSet.clone(); - if(trainingSet.length !== predictions.length) - throw new RangeError("The number of predictions and elements in the dataset must be the same"); - - //var tolerance = 1e-9; - var X = Utils.featureNormalize(new Matrix(trainingSet)).result; - var resultY = Utils.featureNormalize(new Matrix(predictions)); - this.ymean = resultY.means.neg(); - this.ystd = resultY.std; - var Y = resultY.result; - - var rx = X.rows; - var cx = X.columns; - var ry = Y.rows; - var cy = Y.columns; - - if(rx != ry) { - throw new RangeError("dataset cases is not the same as the predictions"); - } - - var ssqXcal = X.clone().mul(X).sum(); // for the r² - var sumOfSquaresY = Y.clone().mul(Y).sum(); - - var n = latentVectors; //Math.max(cx, cy); // components of the pls - var T = Matrix.zeros(rx, n); - var P = Matrix.zeros(cx, n); - var U = Matrix.zeros(ry, n); - var Q = Matrix.zeros(cy, n); - var B = Matrix.zeros(n, n); - var W = P.clone(); - var k = 0; - var R2X = new Array(n); + if(trainingSet.rows !== trainingLabels.length) + throw new RangeError("the size of the training set and the training labels must be the same."); - while(Utils.norm(Y) > tolerance && k < n) { - var transposeX = X.transpose(); - var transposeY = Y.transpose(); + var separatedClasses = separateClasses(trainingSet, trainingLabels); + var calculateProbabilities = new Array(separatedClasses.length); + this.means = new Array(separatedClasses.length); + for(var i = 0; i < separatedClasses.length; ++i) { + var means = Stat.matrix.mean(separatedClasses[i]); + var std = Stat.matrix.standardDeviation(separatedClasses[i], means); - var tIndex = maxSumColIndex(X.clone().mulM(X)); - var uIndex = maxSumColIndex(Y.clone().mulM(Y)); + var logPriorProbability = Math.log(separatedClasses[i].rows / trainingSet.rows); + calculateProbabilities[i] = new Array(means.length + 1); - var t1 = X.getColumnVector(tIndex); - var u = Y.getColumnVector(uIndex); - var t = Matrix.zeros(rx, 1); + calculateProbabilities[i][0] = logPriorProbability; + for(var j = 1; j < means.length + 1; ++j) { + var currentStd = std[j - 1]; + calculateProbabilities[i][j] = [(1 / (C1 * currentStd)), -2*currentStd*currentStd]; + } - while(Utils.norm(t1.clone().sub(t)) > tolerance) { - var w = transposeX.mmul(u); - w.div(Utils.norm(w)); - t = t1; - t1 = X.mmul(w); - var q = transposeY.mmul(t1); - q.div(Utils.norm(q)); - u = Y.mmul(q); - } - - t = t1; - var num = transposeX.mmul(t); - var den = (t.transpose().mmul(t))[0][0]; - var p = num.div(den); - var pnorm = Utils.norm(p); - p.div(pnorm); - t.mul(pnorm); - w.mul(pnorm); - - num = u.transpose().mmul(t); - den = (t.transpose().mmul(t))[0][0]; - var b = (num.div(den))[0][0]; - X.sub(t.mmul(p.transpose())); - Y.sub(t.clone().mul(b).mmul(q.transpose())); - - T.setColumn(k, t); - P.setColumn(k, p); - U.setColumn(k, u); - Q.setColumn(k, q); - W.setColumn(k, w); - - B[k][k] = b; - k++; + this.means[i] = means; } - k--; - T = T.subMatrix(0, T.rows - 1, 0, k); - P = P.subMatrix(0, P.rows - 1, 0, k); - U = U.subMatrix(0, U.rows - 1, 0, k); - Q = Q.subMatrix(0, Q.rows - 1, 0, k); - W = W.subMatrix(0, W.rows - 1, 0, k); - B = B.subMatrix(0, k, 0, k); - - this.R2X = t.transpose().mmul(t).mmul(p.transpose().mmul(p)).divS(ssqXcal)[0][0]; - - // TODO: review of R2Y - //this.R2Y = t.transpose().mmul(t).mul(q[k][0]*q[k][0]).divS(ssqYcal)[0][0]; - - this.ssqYcal = sumOfSquaresY; - this.E = X; - this.F = Y; - this.T = T; - this.P = P; - this.U = U; - this.Q = Q; - this.W = W; - this.B = B; - this.PBQ = P.mmul(B).mmul(Q.transpose()); + this.calculateProbabilities = calculateProbabilities; }; /** - * Function that predict the behavior of the given dataset. - * @param dataset - data to be predicted. - * @returns {Matrix} - predictions of each element of the dataset. + * function that predicts each row of the dataset (must be a matrix). + * + * @param dataset + * @returns {Array} */ - PLS.prototype.predict = function (dataset) { - var X = new Matrix(dataset); - var normalization = Utils.featureNormalize(X); - X = normalization.result; - var Y = X.mmul(this.PBQ); - Y.mulRowVector(this.ystd); - Y.addRowVector(this.ymean); - return Y; + NaiveBayes.prototype.predict = function (dataset) { + if(dataset[0].length === this.calculateProbabilities[0].length) + throw new RangeError('the dataset must have the same features as the training set'); + + var predictions = new Array(dataset.length); + + for(var i = 0; i < predictions.length; ++i) { + predictions[i] = getCurrentClass(dataset[i], this.means, this.calculateProbabilities); + } + + return predictions; }; /** - * Function that returns the explained variance on training of the PLS model. + * Function the retrieves a prediction with one case. + * + * @param currentCase + * @param mean - Precalculated means of each class trained + * @param classes - Precalculated value of each class (Prior probability and probability function of each feature) * @returns {number} */ - PLS.prototype.getExplainedVariance = function () { - return this.R2X; - }; + function getCurrentClass(currentCase, mean, classes) { + var maxProbability = 0; + var predictedClass = -1; - /** - * Load a PLS model from an Object - * @param model - * @returns {PLS} - PLS object from the given model - */ - PLS.load = function (model) { - if(model.modelName !== 'PLS') - throw new RangeError("The current model is invalid!"); + // going through all precalculated values for the classes + for(var i = 0; i < classes.length; ++i) { + var currentProbability = classes[i][0]; // initialize with the prior probability + for(var j = 1; j < classes[0][1].length + 1; ++j) { + currentProbability += calculateLogProbability(currentCase[j - 1], mean[i][j - 1], classes[i][j][0], classes[i][j][1]); + } - return new PLS(true, model); - }; + currentProbability = Math.exp(currentProbability); + if(currentProbability > maxProbability) { + maxProbability = currentProbability; + predictedClass = i; + } + } + + return predictedClass; + } /** - * Function that exports a PLS model to an Object. - * @returns {{modelName: string, ymean: *, ystd: *, PBQ: *}} model. + * Function that export the NaiveBayes model. + * @returns {{modelName: string, means: *, calculateProbabilities: *}} */ - PLS.prototype.export = function () { + NaiveBayes.prototype.export = function () { return { - modelName: "PLS", - E: this.E, - F: this.F, - R2X: this.R2X, - ssqYcal: this.ssqYcal, - ymean: this.ymean, - ystd: this.ystd, - PBQ: this.PBQ, - T: this.T, - P: this.P, - U: this.U, - Q: this.Q, - W: this.W, - B: this.B + modelName: "NaiveBayes", + means: this.means, + calculateProbabilities: this.calculateProbabilities }; }; - -/***/ }, -/* 121 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - var Matrix = __webpack_require__(14); - var Stat = __webpack_require__(122); - /** - * Function that given vector, returns his norm - * @param {Vector} X - * @returns {number} Norm of the vector + * Function that create a Naive Bayes classifier with the given model. + * @param model + * @returns {NaiveBayes} */ - function norm(X) { - return Math.sqrt(X.clone().apply(pow2array).sum()); - } + NaiveBayes.load = function (model) { + if(model.modelName !== 'NaiveBayes') + throw new RangeError("The given model is invalid!"); + + return new NaiveBayes(true, model); + }; /** - * Function that pow 2 each element of a Matrix or a Vector, - * used in the apply method of the Matrix object - * @param i - index i. - * @param j - index j. - * @return The Matrix object modified at the index i, j. - * */ - function pow2array(i, j) { - this[i][j] = this[i][j] * this[i][j]; - return this; + * function that retrieves the probability of the feature given the class. + * @param value - value of the feature. + * @param mean - mean of the feature for the given class. + * @param C1 - precalculated value of (1 / (sqrt(2*pi) * std)). + * @param C2 - precalculated value of (2 * std^2) for the denominator of the exponential. + * @returns {number} + */ + function calculateLogProbability(value, mean, C1, C2) { + var value = value - mean; + return Math.log(C1 * Math.exp((value * value) / C2)) } /** - * Function that normalize the dataset and return the means and - * standard deviation of each feature. - * @param dataset - * @returns {{result: Matrix, means: (*|number), std: Matrix}} dataset normalized, means - * and standard deviations + * Function that retuns an array of matrices of the cases that belong to each class. + * @param X - dataset + * @param y - predictions + * @returns {Array} */ - function featureNormalize(dataset) { - var means = Stat.matrix.mean(dataset); - var std = Matrix.rowVector(Stat.matrix.standardDeviation(dataset, means, true)); - means = Matrix.rowVector(means); + function separateClasses(X, y) { + var features = X.columns; - var result = dataset.addRowVector(means.neg()); - return {result: result.divRowVector(std), means: means, std: std}; + var classes = 0; + var totalPerClasses = new Array(100); // max upperbound of classes + for (var i = 0; i < y.length; i++) { + if(totalPerClasses[y[i]] === undefined) { + totalPerClasses[y[i]] = 0; + classes++; + } + totalPerClasses[y[i]]++; + } + var separatedClasses = new Array(classes); + var currentIndex = new Array(classes); + for(i = 0; i < classes; ++i) { + separatedClasses[i] = new Matrix(totalPerClasses[i], features); + currentIndex[i] = 0; + } + for(i = 0; i < X.rows; ++i) { + separatedClasses[y[i]].setRow(currentIndex[y[i]], X.getRow(i)); + currentIndex[y[i]]++; + } + return separatedClasses; } - module.exports = { - norm: norm, - pow2array: pow2array, - featureNormalize: featureNormalize - }; - - /***/ }, -/* 122 */ +/* 138 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; - exports.array = __webpack_require__(123); - exports.matrix = __webpack_require__(124); + exports.array = __webpack_require__(139); + exports.matrix = __webpack_require__(140); /***/ }, -/* 123 */ +/* 139 */ /***/ function(module, exports) { 'use strict'; @@ -13522,11 +19968,11 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 124 */ +/* 140 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; - var arrayStat = __webpack_require__(123); + var arrayStat = __webpack_require__(139); // https://github.com/accord-net/framework/blob/development/Sources/Accord.Statistics/Tools.cs @@ -14048,13 +20494,307 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 125 */ +/* 141 */ +/***/ function(module, exports, __webpack_require__) { + + module.exports = exports = __webpack_require__(142); + exports.Utils = __webpack_require__(143); + exports.OPLS = __webpack_require__(144); + + +/***/ }, +/* 142 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var Matrix = __webpack_require__(14); + var Utils = __webpack_require__(143); + + class PLS { + constructor(reload, model) { + if (reload) { + this.xmean = model.xmean; + this.xstd = model.xstd; + this.ymean = model.ymean; + this.ystd = model.ystd; + this.PBQ = Matrix.checkMatrix(model.PBQ); + this.R2X = model.R2X; + } + } + + /** + * Fits the model with the given data and predictions, in this function is calculated the + * following outputs: + * + * T - Score matrix of X + * P - Loading matrix of X + * U - Score matrix of Y + * Q - Loading matrix of Y + * B - Matrix of regression coefficient + * W - Weight matrix of X + * + * @param {Matrix} trainingSet - Dataset to be apply the model + * @param {Matrix} predictions - Predictions over each case of the dataset + * @param {Object} options - recieves the latentVectors and the tolerance of each step of the PLS + */ + train(trainingSet, predictions, options) { + if(options === undefined) options = {}; + + var latentVectors = options.latentVectors; + if (latentVectors === undefined) { + latentVectors = Math.min(trainingSet.length - 1, trainingSet[0].length); + } + + var tolerance = options.tolerance; + if (tolerance === undefined) { + tolerance = 1e-5; + } + + if (trainingSet.length !== predictions.length) + throw new RangeError('The number of predictions and elements in the dataset must be the same'); + + var resultX = Utils.featureNormalize(trainingSet); + this.xmean = resultX.means; + this.xstd = resultX.std; + var X = resultX.result; + + var resultY = Utils.featureNormalize(predictions); + this.ymean = resultY.means; + this.ystd = resultY.std; + var Y = resultY.result; + + var rx = X.rows; + var cx = X.columns; + var ry = Y.rows; + var cy = Y.columns; + + if(rx != ry) { + throw new RangeError('dataset cases is not the same as the predictions'); + } + + var ssqXcal = X.clone().mul(X).sum(); // for the r² + var sumOfSquaresY = Y.clone().mul(Y).sum(); + + var n = latentVectors; //Math.max(cx, cy); // components of the pls + var T = Matrix.zeros(rx, n); + var P = Matrix.zeros(cx, n); + var U = Matrix.zeros(ry, n); + var Q = Matrix.zeros(cy, n); + var B = Matrix.zeros(n, n); + var W = P.clone(); + var k = 0; + + while(Utils.norm(Y) > tolerance && k < n) { + var transposeX = X.transpose(); + var transposeY = Y.transpose(); + + var tIndex = maxSumColIndex(X.clone().mulM(X)); + var uIndex = maxSumColIndex(Y.clone().mulM(Y)); + + var t1 = X.getColumnVector(tIndex); + var u = Y.getColumnVector(uIndex); + var t = Matrix.zeros(rx, 1); + + while(Utils.norm(t1.clone().sub(t)) > tolerance) { + var w = transposeX.mmul(u); + w.div(Utils.norm(w)); + t = t1; + t1 = X.mmul(w); + var q = transposeY.mmul(t1); + q.div(Utils.norm(q)); + u = Y.mmul(q); + } + + t = t1; + var num = transposeX.mmul(t); + var den = (t.transpose().mmul(t))[0][0]; + var p = num.div(den); + var pnorm = Utils.norm(p); + p.div(pnorm); + t.mul(pnorm); + w.mul(pnorm); + + num = u.transpose().mmul(t); + den = (t.transpose().mmul(t))[0][0]; + var b = (num.div(den))[0][0]; + X.sub(t.mmul(p.transpose())); + Y.sub(t.clone().mul(b).mmul(q.transpose())); + + T.setColumn(k, t); + P.setColumn(k, p); + U.setColumn(k, u); + Q.setColumn(k, q); + W.setColumn(k, w); + + B[k][k] = b; + k++; + } + + k--; + T = T.subMatrix(0, T.rows - 1, 0, k); + P = P.subMatrix(0, P.rows - 1, 0, k); + U = U.subMatrix(0, U.rows - 1, 0, k); + Q = Q.subMatrix(0, Q.rows - 1, 0, k); + W = W.subMatrix(0, W.rows - 1, 0, k); + B = B.subMatrix(0, k, 0, k); + + // TODO: review of R2Y + //this.R2Y = t.transpose().mmul(t).mul(q[k][0]*q[k][0]).divS(ssqYcal)[0][0]; + + this.ssqYcal = sumOfSquaresY; + this.E = X; + this.F = Y; + this.T = T; + this.P = P; + this.U = U; + this.Q = Q; + this.W = W; + this.B = B; + this.PBQ = P.mmul(B).mmul(Q.transpose()); + this.R2X = t.transpose().mmul(t).mmul(p.transpose().mmul(p)).div(ssqXcal)[0][0]; + } + + /** + * Predicts the behavior of the given dataset. + * @param dataset - data to be predicted. + * @returns {Matrix} - predictions of each element of the dataset. + */ + predict(dataset) { + var X = Matrix.checkMatrix(dataset); + X = X.subRowVector(this.xmean).divRowVector(this.xstd); + var Y = X.mmul(this.PBQ); + Y = Y.mulRowVector(this.ystd).addRowVector(this.ymean); + return Y; + } + + /** + * Returns the explained variance on training of the PLS model + * @return {number} + */ + getExplainedVariance() { + return this.R2X; + } + + toJSON() { + return { + name: 'PLS', + R2X: this.R2X, + xmean: this.xmean, + xstd: this.xstd, + ymean: this.ymean, + ystd: this.ystd, + PBQ: this.PBQ, + }; + } + + /** + * Load a PLS model from a JSON Object + * @param model + * @return {PLS} - PLS object from the given model + */ + static load(model) { + if (model.name !== 'PLS') + throw new RangeError('Invalid model: ' + model.name); + return new PLS(true, model); + } + } + + module.exports = PLS; + + /** + * Retrieves the sum at the column of the given matrix. + * @param matrix + * @param column + * @returns {number} + */ + function getColSum(matrix, column) { + var sum = 0; + for (var i = 0; i < matrix.rows; i++) { + sum += matrix[i][column]; + } + return sum; + } + + /** + * Function that returns the index where the sum of each + * column vector is maximum. + * @param {Matrix} data + * @returns {number} index of the maximum + */ + function maxSumColIndex(data) { + var maxIndex = 0; + var maxSum = -Infinity; + for(var i = 0; i < data.columns; ++i) { + var currentSum = getColSum(data, i); + if(currentSum > maxSum) { + maxSum = currentSum; + maxIndex = i; + } + } + return maxIndex; + } + + +/***/ }, +/* 143 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + const Matrix = __webpack_require__(14); + const Stat = __webpack_require__(5); + + /** + * Function that given vector, returns his norm + * @param {Vector} X + * @returns {number} Norm of the vector + */ + function norm(X) { + return Math.sqrt(X.clone().apply(pow2array).sum()); + } + + /** + * Function that pow 2 each element of a Matrix or a Vector, + * used in the apply method of the Matrix object + * @param i - index i. + * @param j - index j. + * @return The Matrix object modified at the index i, j. + * */ + function pow2array(i, j) { + this[i][j] = this[i][j] * this[i][j]; + return this; + } + + /** + * Function that normalize the dataset and return the means and + * standard deviation of each feature. + * @param dataset + * @returns {{result: Matrix, means: (*|number), std: Matrix}} dataset normalized, means + * and standard deviations + */ + function featureNormalize(dataset) { + var means = Stat.matrix.mean(dataset); + var std = Stat.matrix.standardDeviation(dataset, means, true); + var result = Matrix.checkMatrix(dataset).subRowVector(means); + return {result: result.divRowVector(std), means: means, std: std}; + } + + module.exports = { + norm: norm, + pow2array: pow2array, + featureNormalize: featureNormalize + }; + + +/***/ }, +/* 144 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; var Matrix = __webpack_require__(14); - var Utils = __webpack_require__(121); + var Utils = __webpack_require__(143); module.exports = OPLS; @@ -14132,13 +20872,13 @@ return /******/ (function(modules) { // webpackBootstrap }; /***/ }, -/* 126 */ +/* 145 */ /***/ function(module, exports, __webpack_require__) { - module.exports = __webpack_require__(127); + module.exports = __webpack_require__(146); /***/ }, -/* 127 */ +/* 146 */ /***/ function(module, exports) { 'use strict'; @@ -14330,24 +21070,24 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 128 */ +/* 147 */ /***/ function(module, exports, __webpack_require__) { - exports.agnes = __webpack_require__(129); - exports.diana = __webpack_require__(137); + exports.agnes = __webpack_require__(148); + exports.diana = __webpack_require__(156); //exports.birch = require('./birch'); //exports.cure = require('./cure'); //exports.chameleon = require('./chameleon'); /***/ }, -/* 129 */ +/* 148 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; - var euclidean = __webpack_require__(130); - var ClusterLeaf = __webpack_require__(131); - var Cluster = __webpack_require__(132); + var euclidean = __webpack_require__(149); + var ClusterLeaf = __webpack_require__(150); + var Cluster = __webpack_require__(151); /** * @param cluster1 @@ -14579,7 +21319,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = agnes; /***/ }, -/* 130 */ +/* 149 */ /***/ function(module, exports) { 'use strict'; @@ -14601,13 +21341,13 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 131 */ +/* 150 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; - var Cluster = __webpack_require__(132); - var util = __webpack_require__(133); + var Cluster = __webpack_require__(151); + var util = __webpack_require__(152); function ClusterLeaf (index) { Cluster.call(this); @@ -14622,7 +21362,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 132 */ +/* 151 */ /***/ function(module, exports) { 'use strict'; @@ -14693,7 +21433,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 133 */ +/* 152 */ /***/ function(module, exports, __webpack_require__) { /* WEBPACK VAR INJECTION */(function(global, process) {// Copyright Joyent, Inc. and other Node contributors. @@ -15221,7 +21961,7 @@ return /******/ (function(modules) { // webpackBootstrap } exports.isPrimitive = isPrimitive; - exports.isBuffer = __webpack_require__(135); + exports.isBuffer = __webpack_require__(154); function objectToString(o) { return Object.prototype.toString.call(o); @@ -15265,7 +22005,7 @@ return /******/ (function(modules) { // webpackBootstrap * prototype. * @param {function} superCtor Constructor function to inherit prototype from. */ - exports.inherits = __webpack_require__(136); + exports.inherits = __webpack_require__(155); exports._extend = function(origin, add) { // Don't do anything if add isn't an object @@ -15283,10 +22023,10 @@ return /******/ (function(modules) { // webpackBootstrap return Object.prototype.hasOwnProperty.call(obj, prop); } - /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()), __webpack_require__(134))) + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()), __webpack_require__(153))) /***/ }, -/* 134 */ +/* 153 */ /***/ function(module, exports) { // shim for using process in browser @@ -15383,7 +22123,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 135 */ +/* 154 */ /***/ function(module, exports) { module.exports = function isBuffer(arg) { @@ -15394,7 +22134,7 @@ return /******/ (function(modules) { // webpackBootstrap } /***/ }, -/* 136 */ +/* 155 */ /***/ function(module, exports) { if (typeof Object.create === 'function') { @@ -15423,14 +22163,14 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 137 */ +/* 156 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; - var euclidean = __webpack_require__(130); - var ClusterLeaf = __webpack_require__(131); - var Cluster = __webpack_require__(132); + var euclidean = __webpack_require__(149); + var ClusterLeaf = __webpack_require__(150); + var Cluster = __webpack_require__(151); /** * @param {Array >} cluster1 @@ -15719,13 +22459,13 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = diana; /***/ }, -/* 138 */ +/* 157 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; - var NodeSquare = __webpack_require__(139), - NodeHexagonal = __webpack_require__(140); + var NodeSquare = __webpack_require__(158), + NodeHexagonal = __webpack_require__(159); var defaultOptions = { fields: 3, @@ -16145,7 +22885,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = SOM; /***/ }, -/* 139 */ +/* 158 */ /***/ function(module, exports) { function NodeSquare(x, y, weights, som) { @@ -16256,10 +22996,10 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = NodeSquare; /***/ }, -/* 140 */ +/* 159 */ /***/ function(module, exports, __webpack_require__) { - var NodeSquare = __webpack_require__(139); + var NodeSquare = __webpack_require__(158); function NodeHexagonal(x, y, weights, som) { @@ -16291,19 +23031,19 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = NodeHexagonal; /***/ }, -/* 141 */ +/* 160 */ /***/ function(module, exports, __webpack_require__) { - module.exports = __webpack_require__(142); + module.exports = __webpack_require__(161); /***/ }, -/* 142 */ +/* 161 */ /***/ function(module, exports, __webpack_require__) { "use strict"; - var Layer = __webpack_require__(143); + var Layer = __webpack_require__(162); var Matrix = __webpack_require__(14); module.exports = FeedforwardNeuralNetwork; @@ -16482,7 +23222,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 143 */ +/* 162 */ /***/ function(module, exports, __webpack_require__) { "use strict"; diff --git a/package.json b/package.json index 08930f2..872bcab 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ml", - "version": "0.9.1", + "version": "0.10.0", "description": "Machine learning tools", "main": "src/index.js", "scripts": { @@ -50,7 +50,7 @@ "ml-stat": "^1.1.0", "ml-svm": "^1.0.2", "ml-xsadd": "^1.0.0", - "ml-optimize-lorentzian":"^0.1.2" + "ml-optimize-lorentzian": "^0.1.2" }, "devDependencies": { "cheminfo-tools": "^1.0.1",