diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..bcb1ce190 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,23 @@ +language: node_js +node_js: + - '4' +install: + - npm install +script: + - npm run test +addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - libcairo2-dev + - libjpeg8-dev + - libpango1.0-dev + - libgif-dev + - g++-4.9 +env: + - CXX=g++-4.9 +before_install: + - if [[ $TRAVIS_NODE_VERSION == 0.8 ]]; then npm install -g npm@1.4.28; fi + - npm explore npm -g -- npm install node-gyp@latest +sudo: false \ No newline at end of file diff --git a/Gruntfile.js b/Gruntfile.js deleted file mode 100644 index f3c73d8f1..000000000 --- a/Gruntfile.js +++ /dev/null @@ -1,83 +0,0 @@ -/* - * To run this file: - * `npm install --dev` - * `npm install -g grunt` - * - * `grunt --help` - */ - -var fs = require('fs'); -var browserify = require('browserify'); -var pkg = require('./package.json'); -var grunt = require('grunt'); -require('load-grunt-tasks')(grunt); - -module.exports = function(grunt) { - grunt.initConfig({ - babel: { - options: { - sourceMap: true, - presets: ['es2015'] - }, - dist: { - files: [{ - expand: true, - cwd: 'src/', - src: ['*.js'], - dest: 'dist/' - },{ - expand: true, - cwd: 'src/utilities', - src: ['*.js'], - dest: 'dist/utilities' - }] - } - }, - mochaTest: { - test: { - options: { - style: 'bdd', - reporter: 'spec', - require: 'babel-register' - }, - src: [ - 'test/cross-validation/*.js', - 'test/unit/*.js' - ] - } - }, - pkg: grunt.file.readJSON('package.json'), - browserify: { - options: { - banner: '/*\n' + grunt.file.read('LICENSE') + '*/', - version: grunt.file.readJSON('package.json').version - }, - browser: { - src: ['dist/*.js', 'dist/utilities/*.js'], - dest: 'browser.js' - } - }, - uglify: { - options: { - banner: '/*\n' + grunt.file.read('LICENSE') + '*/', - }, - dist: { - files: { - 'browser.min.js': ['browser.js'] - } - } - } - }); - - //we need to keep these up to date - if (grunt.file.readJSON('package.json').version !== grunt.file.readJSON('bower.json').version) { - console.log('Error: bower.json & package.json version mismatch'); - process.exit(0); - } - - grunt.registerTask('test', 'mochaTest'); - grunt.loadNpmTasks('grunt-mocha-test'); - grunt.loadNpmTasks('grunt-browserify'); - grunt.loadNpmTasks('grunt-contrib-uglify'); - grunt.registerTask('default', ['babel', 'test', 'browserify', 'uglify']); -}; diff --git a/bower.json b/bower.json index 49eafa984..6df64a7ca 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "brain.js", - "version": "1.0.0-alpha", + "version": "1.0.0", "homepage": "https://github.com/harthur-org/brain.js", "authors": [ "Heather Arthur " @@ -11,7 +11,7 @@ "artificial-intelligence", "brain", "brainjs", - "brain,js", + "brain.js", "feed forward", "neural network", "classifier", @@ -19,7 +19,7 @@ "network", "neural-networks", "machine-learning", - "synpapse" + "synapse" ], "main": "./browser.min.js", "ignore": [ diff --git a/browser.js b/browser.js index 240c1f718..115e0017d 100644 --- a/browser.js +++ b/browser.js @@ -1,65 +1,111 @@ -/* -Copyright (c) 2010-2016 Heather Arthur - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/ +/** + * Modules in this bundle + * @license + * + * brain.js: + * license: MIT (http://opensource.org/licenses/MIT) + * author: Heather Arthur + * homepage: https://github.com/harthur-org/brain.js#readme + * version: 1.0.0 + * + * base64-js: + * license: MIT (http://opensource.org/licenses/MIT) + * author: T. Jameson Little + * maintainers: beatgammit , feross + * homepage: https://github.com/beatgammit/base64-js#readme + * version: 1.1.2 + * + * buffer: + * license: MIT (http://opensource.org/licenses/MIT) + * author: Feross Aboukhadijeh + * maintainers: feross + * contributors: Romain Beauxis , James Halliday + * homepage: https://github.com/feross/buffer + * version: 4.9.1 + * + * buffer-shims: + * license: MIT (http://opensource.org/licenses/MIT) + * maintainers: cwmma + * homepage: https://github.com/calvinmetcalf/buffer-shims#readme + * version: 1.0.0 + * + * core-util-is: + * license: MIT (http://opensource.org/licenses/MIT) + * author: Isaac Z. Schlueter + * maintainers: isaacs + * homepage: https://github.com/isaacs/core-util-is#readme + * version: 1.0.2 + * + * events: + * license: MIT (http://opensource.org/licenses/MIT) + * author: Irakli Gozalishvili + * maintainers: gozala , defunctzombie + * homepage: https://github.com/Gozala/events#readme + * version: 1.1.1 + * + * ieee754: + * license: MIT (http://opensource.org/licenses/MIT) + * author: Feross Aboukhadijeh + * maintainers: feross + * contributors: Romain Beauxis + * homepage: https://github.com/feross/ieee754#readme + * version: 1.1.6 + * + * inherits: + * license: ISC (http://opensource.org/licenses/ISC) + * maintainers: isaacs + * homepage: https://github.com/isaacs/inherits#readme + * version: 2.0.2 + * + * isarray: + * license: MIT (http://opensource.org/licenses/MIT) + * author: Julian Gruber + * maintainers: juliangruber + * homepage: https://github.com/juliangruber/isarray + * version: 1.0.0 + * + * process: + * license: MIT (http://opensource.org/licenses/MIT) + * author: Roman Shtylman + * maintainers: coolaj86 , cwmma , defunctzombie + * homepage: https://github.com/shtylman/node-process#readme + * version: 0.11.9 + * + * process-nextick-args: + * license: MIT (http://opensource.org/licenses/MIT) + * maintainers: cwmma + * homepage: https://github.com/calvinmetcalf/process-nextick-args + * version: 1.0.7 + * + * readable-stream: + * license: MIT (http://opensource.org/licenses/MIT) + * maintainers: isaacs , tootallnate , rvagg , cwmma + * homepage: https://github.com/nodejs/readable-stream#readme + * version: 2.1.5 + * + * stream-browserify: + * license: MIT (http://opensource.org/licenses/MIT) + * author: James Halliday + * maintainers: substack , feross , stevemao + * homepage: https://github.com/substack/stream-browserify + * version: 2.0.1 + * + * string_decoder: + * license: MIT (http://opensource.org/licenses/MIT) + * maintainers: substack , rvagg + * homepage: https://github.com/rvagg/string_decoder + * version: 0.10.31 + * + * util-deprecate: + * license: MIT (http://opensource.org/licenses/MIT) + * author: Nathan Rajlich + * maintainers: tootallnate + * homepage: https://github.com/TooTallNate/util-deprecate + * version: 1.0.2 + * + * This header is generated by licensify (https://github.com/twada/licensify) + */ (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 0) { + layers[layer][node].bias = this.biases[layer][j]; + layers[layer][node].weights = {}; + for (var k in layers[layer - 1]) { + var index = k; + if (layer == 1 && this.inputLookup) { + index = this.inputLookup[k]; + } + layers[layer][node].weights[k] = this.weights[layer][j][index]; + } + } + } + } + return { layers: layers, outputLookup: !!this.outputLookup, inputLookup: !!this.inputLookup }; + } + + /** + * + * @param json + * @returns {NeuralNetwork} + */ + + }, { + key: 'fromJSON', + value: function fromJSON(json) { + var size = json.layers.length; + this.outputLayer = size - 1; + + this.sizes = new Array(size); + this.weights = new Array(size); + this.biases = new Array(size); + this.outputs = new Array(size); + + for (var i = 0; i <= this.outputLayer; i++) { + var layer = json.layers[i]; + if (i == 0 && (!layer[0] || json.inputLookup)) { + this.inputLookup = _lookup2.default.lookupFromHash(layer); + } else if (i == this.outputLayer && (!layer[0] || json.outputLookup)) { + this.outputLookup = _lookup2.default.lookupFromHash(layer); + } + + var nodes = Object.keys(layer); + this.sizes[i] = nodes.length; + this.weights[i] = []; + this.biases[i] = []; + this.outputs[i] = []; + + for (var j in nodes) { + var node = nodes[j]; + this.biases[i][j] = layer[node].bias; + this.weights[i][j] = (0, _toArray2.default)(layer[node].weights); + } + } + return this; + } + + /** + * + * @returns {Function} + */ + + }, { + key: 'toFunction', + value: function toFunction() { + var json = this.toJSON(); + var jsonString = JSON.stringify(json); + // return standalone function that mimics run() + return new Function('input', '\n var net = ' + jsonString + ';\n for (var i = 1; i < net.layers.length; i++) {\n var layer = net.layers[i];\n var output = {};\n \n for (var id in layer) {\n var node = layer[id];\n var sum = node.bias;\n \n for (var iid in node.weights) {\n sum += node.weights[iid] * input[iid];\n }\n output[id] = (1 / (1 + Math.exp(-sum)));\n }\n input = output;\n }\n return output;\n '); + } + + /** + * This will create a TrainStream (WriteStream) for us to send the training data to. + * @param opts training options + * @returns {TrainStream|*} + */ + + }, { + key: 'createTrainStream', + value: function createTrainStream(opts) { + opts = opts || {}; + opts.neuralNetwork = this; + this.trainStream = new _trainStream2.default(opts); + return this.trainStream; + } + }]); + + return NeuralNetwork; +}(); + +exports.default = NeuralNetwork; + +},{"./lookup":3,"./train-stream":32,"./utilities/max":33,"./utilities/mse":34,"./utilities/randos":38,"./utilities/range":39,"./utilities/to-array":40,"./utilities/zeros":42}],5:[function(require,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; }; }(); + +var _matrix = require('./matrix'); + +var _matrix2 = _interopRequireDefault(_matrix); + +var _randomMatrix = require('./matrix/random-matrix'); + +var _randomMatrix2 = _interopRequireDefault(_randomMatrix); + +var _rnn = require('./rnn'); + +var _rnn2 = _interopRequireDefault(_rnn); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var GRU = function (_RNN) { + _inherits(GRU, _RNN); + + function GRU() { + _classCallCheck(this, GRU); + + return _possibleConstructorReturn(this, (GRU.__proto__ || Object.getPrototypeOf(GRU)).apply(this, arguments)); + } + + _createClass(GRU, [{ + key: 'getModel', + value: function getModel(hiddenSize, prevSize) { + return { + // update Gate + //wzxh + updateGateInputMatrix: new _randomMatrix2.default(hiddenSize, prevSize, 0.08), + //wzhh + updateGateHiddenMatrix: new _randomMatrix2.default(hiddenSize, hiddenSize, 0.08), + //bz + updateGateBias: new _matrix2.default(hiddenSize, 1), + + // reset Gate + //wrxh + resetGateInputMatrix: new _randomMatrix2.default(hiddenSize, prevSize, 0.08), + //wrhh + resetGateHiddenMatrix: new _randomMatrix2.default(hiddenSize, hiddenSize, 0.08), + //br + resetGateBias: new _matrix2.default(hiddenSize, 1), + + // cell write parameters + //wcxh + cellWriteInputMatrix: new _randomMatrix2.default(hiddenSize, prevSize, 0.08), + //wchh + cellWriteHiddenMatrix: new _randomMatrix2.default(hiddenSize, hiddenSize, 0.08), + //bc + cellWriteBias: new _matrix2.default(hiddenSize, 1) + }; + } + + /** + * + * @param {Equation} equation + * @param {Matrix} inputMatrix + * @param {Matrix} previousResult + * @param {Object} hiddenLayer + * @returns {Matrix} + */ + + }, { + key: 'getEquation', + value: function getEquation(equation, inputMatrix, previousResult, hiddenLayer) { + var sigmoid = equation.sigmoid.bind(equation); + var add = equation.add.bind(equation); + var multiply = equation.multiply.bind(equation); + var multiplyElement = equation.multiplyElement.bind(equation); + var tanh = equation.tanh.bind(equation); + var allOnes = equation.allOnes.bind(equation); + var cloneNegative = equation.cloneNegative.bind(equation); + + // update gate + var updateGate = sigmoid(add(add(multiply(hiddenLayer.updateGateInputMatrix, inputMatrix), multiply(hiddenLayer.updateGateHiddenMatrix, previousResult)), hiddenLayer.updateGateBias)); + + // reset gate + var resetGate = sigmoid(add(add(multiply(hiddenLayer.resetGateInputMatrix, inputMatrix), multiply(hiddenLayer.resetGateHiddenMatrix, previousResult)), hiddenLayer.resetGateBias)); + + // cell + var cell = tanh(add(add(multiply(hiddenLayer.cellWriteInputMatrix, inputMatrix), multiply(hiddenLayer.cellWriteHiddenMatrix, multiplyElement(resetGate, previousResult))), hiddenLayer.cellWriteBias)); + + // compute hidden state as gated, saturated cell activations + // negate updateGate + return add(multiplyElement(add(allOnes(updateGate.rows, updateGate.columns), cloneNegative(updateGate)), cell), multiplyElement(previousResult, updateGate)); + } + }]); + + return GRU; +}(_rnn2.default); + +exports.default = GRU; + +},{"./matrix":13,"./matrix/random-matrix":20,"./rnn":31}],6:[function(require,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; }; }(); + +var _matrix = require('./matrix'); + +var _matrix2 = _interopRequireDefault(_matrix); + +var _randomMatrix = require('./matrix/random-matrix'); + +var _randomMatrix2 = _interopRequireDefault(_randomMatrix); + +var _rnn = require('./rnn'); + +var _rnn2 = _interopRequireDefault(_rnn); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var LSTM = function (_RNN) { + _inherits(LSTM, _RNN); + + function LSTM() { + _classCallCheck(this, LSTM); + + return _possibleConstructorReturn(this, (LSTM.__proto__ || Object.getPrototypeOf(LSTM)).apply(this, arguments)); + } + + _createClass(LSTM, [{ + key: 'getModel', + value: function getModel(hiddenSize, prevSize) { + return { + // gates parameters + //wix + inputMatrix: new _randomMatrix2.default(hiddenSize, prevSize, 0.08), + //wih + inputHidden: new _randomMatrix2.default(hiddenSize, hiddenSize, 0.08), + //bi + inputBias: new _matrix2.default(hiddenSize, 1), + + //wfx + forgetMatrix: new _randomMatrix2.default(hiddenSize, prevSize, 0.08), + //wfh + forgetHidden: new _randomMatrix2.default(hiddenSize, hiddenSize, 0.08), + //bf + forgetBias: new _matrix2.default(hiddenSize, 1), + + //wox + outputMatrix: new _randomMatrix2.default(hiddenSize, prevSize, 0.08), + //woh + outputHidden: new _randomMatrix2.default(hiddenSize, hiddenSize, 0.08), + //bo + outputBias: new _matrix2.default(hiddenSize, 1), + + // cell write params + //wcx + cellActivationMatrix: new _randomMatrix2.default(hiddenSize, prevSize, 0.08), + //wch + cellActivationHidden: new _randomMatrix2.default(hiddenSize, hiddenSize, 0.08), + //bc + cellActivationBias: new _matrix2.default(hiddenSize, 1) + }; + } + + /** + * + * @param {Equation} equation + * @param {Matrix} inputMatrix + * @param {Matrix} previousResult + * @param {Object} hiddenLayer + * @returns {Matrix} + */ + + }, { + key: 'getEquation', + value: function getEquation(equation, inputMatrix, previousResult, hiddenLayer) { + var sigmoid = equation.sigmoid.bind(equation); + var add = equation.add.bind(equation); + var multiply = equation.multiply.bind(equation); + var multiplyElement = equation.multiplyElement.bind(equation); + var tanh = equation.tanh.bind(equation); + + var inputGate = sigmoid(add(add(multiply(hiddenLayer.inputMatrix, inputMatrix), multiply(hiddenLayer.inputHidden, previousResult)), hiddenLayer.inputBias)); + + var forgetGate = sigmoid(add(add(multiply(hiddenLayer.forgetMatrix, inputMatrix), multiply(hiddenLayer.forgetHidden, previousResult)), hiddenLayer.forgetBias)); + + // output gate + var outputGate = sigmoid(add(add(multiply(hiddenLayer.outputMatrix, inputMatrix), multiply(hiddenLayer.outputHidden, previousResult)), hiddenLayer.outputBias)); + + // write operation on cells + var cellWrite = tanh(add(add(multiply(hiddenLayer.cellActivationMatrix, inputMatrix), multiply(hiddenLayer.cellActivationHidden, previousResult)), hiddenLayer.cellActivationBias)); + + // compute new cell activation + var retainCell = multiplyElement(forgetGate, previousResult); // what do we keep from cell + var writeCell = multiplyElement(inputGate, cellWrite); // what do we write to cell + var cell = add(retainCell, writeCell); // new cell contents + + // compute hidden state as gated, saturated cell activations + return multiplyElement(outputGate, tanh(cell)); + } + }]); + + return LSTM; +}(_rnn2.default); + +exports.default = LSTM; + +},{"./matrix":13,"./matrix/random-matrix":20,"./rnn":31}],7:[function(require,module,exports){ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = addB; +/** + * adds {from} recurrence to {left} and {right} recurrence + * @param {Matrix} product + * @param {Matrix} left + * @param {Matrix} right + */ +function addB(product, left, right) { + for (var i = 0, max = product.recurrence.length; i < max; i++) { + left.recurrence[i] += product.recurrence[i]; + right.recurrence[i] += product.recurrence[i]; + } +} + +},{}],8:[function(require,module,exports){ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = add; +/** + * add {left} and {right} matrix weights into {into} + * @param {Matrix} product + * @param {Matrix} left + * @param {Matrix} right + */ +function add(product, left, right) { + for (var i = 0, max = left.weights.length; i < max; i++) { + product.weights[i] = left.weights[i] + right.weights[i]; + } +} + +},{}],9:[function(require,module,exports){ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = allOnes; +/** + * makes matrix weights and recurrence all ones + * @param {Matrix} product + */ +function allOnes(product) { + for (var i = 0, max = product.weights.length; i < max; i++) { + product.weights[i] = 1; + product.recurrence[i] = 0; + } +} + +},{}],10:[function(require,module,exports){ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = cloneNegative; +/** + * + * @param {Matrix} product + * @param {Matrix} left + */ +function cloneNegative(product, left) { + product.rows = parseInt(left.rows); + product.columns = parseInt(left.columns); + product.weights = left.weights.slice(0); + product.recurrence = left.recurrence.slice(0); + for (var i = 0, max = left.weights.length; i < max; i++) { + product.weights[i] = -left.weights[i]; + product.recurrence[i] = 0; + } +} + +},{}],11:[function(require,module,exports){ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = copy; +/* + * + * @param {Matrix} product + * @param {Matrix} left + */ +function copy(product, left) { + product.rows = parseInt(left.rows); + product.columns = parseInt(left.columns); + product.weights = left.weights.slice(0); + product.recurrence = left.recurrence.slice(0); +} + +},{}],12:[function(require,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; }; }(); + +var _ = require('./'); + +var _2 = _interopRequireDefault(_); + +var _onesMatrix = require('./ones-matrix'); + +var _onesMatrix2 = _interopRequireDefault(_onesMatrix); + +var _copy = require('./copy'); + +var _copy2 = _interopRequireDefault(_copy); + +var _cloneNegative2 = require('./clone-negative'); + +var _cloneNegative3 = _interopRequireDefault(_cloneNegative2); + +var _add2 = require('./add'); + +var _add3 = _interopRequireDefault(_add2); + +var _addB = require('./add-b'); + +var _addB2 = _interopRequireDefault(_addB); + +var _allOnes2 = require('./all-ones'); + +var _allOnes3 = _interopRequireDefault(_allOnes2); + +var _multiply2 = require('./multiply'); + +var _multiply3 = _interopRequireDefault(_multiply2); + +var _multiplyB = require('./multiply-b'); + +var _multiplyB2 = _interopRequireDefault(_multiplyB); + +var _multiplyElement2 = require('./multiply-element'); + +var _multiplyElement3 = _interopRequireDefault(_multiplyElement2); + +var _multiplyElementB = require('./multiply-element-b'); + +var _multiplyElementB2 = _interopRequireDefault(_multiplyElementB); + +var _relu2 = require('./relu'); + +var _relu3 = _interopRequireDefault(_relu2); + +var _reluB = require('./relu-b'); + +var _reluB2 = _interopRequireDefault(_reluB); + +var _rowPluck = require('./row-pluck'); + +var _rowPluck2 = _interopRequireDefault(_rowPluck); + +var _rowPluckB = require('./row-pluck-b'); + +var _rowPluckB2 = _interopRequireDefault(_rowPluckB); + +var _sigmoid2 = require('./sigmoid'); + +var _sigmoid3 = _interopRequireDefault(_sigmoid2); + +var _sigmoidB = require('./sigmoid-b'); + +var _sigmoidB2 = _interopRequireDefault(_sigmoidB); + +var _tanh2 = require('./tanh'); + +var _tanh3 = _interopRequireDefault(_tanh2); + +var _tanhB = require('./tanh-b'); + +var _tanhB2 = _interopRequireDefault(_tanhB); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var Equation = function () { + function Equation() { + _classCallCheck(this, Equation); + + this.inputRow = 0; + this.states = []; + this.previousResults = []; + this.previousResultInputs = []; + this.allMatrices = []; + } + + /** + * connects two matrices together by add + * @param {Matrix} left + * @param {Matrix} right + * @returns {Matrix} + */ + + + _createClass(Equation, [{ + key: 'add', + value: function add(left, right) { + if (left.weights.length !== right.weights.length) { + throw new Error('misaligned matrices'); + } + var product = new _2.default(left.rows, left.columns); + this.allMatrices.push(product); + this.states.push({ + left: left, + right: right, + product: product, + forwardFn: _add3.default, + backpropagationFn: _addB2.default + }); + return product; + } + + /** + * + * @param {Number} rows + * @param {Number} columns + * @returns {Matrix} + */ + + }, { + key: 'allOnes', + value: function allOnes(rows, columns) { + var product = new _2.default(rows, columns); + this.allMatrices.push(product); + this.states.push({ + left: product, + product: product, + forwardFn: _allOnes3.default + }); + return product; + } + + /** + * + * @param {Matrix} m + * @returns {Matrix} + */ + + }, { + key: 'cloneNegative', + value: function cloneNegative(m) { + var product = new _2.default(m.rows, m.columns); + this.allMatrices.push(product); + this.states.push({ + left: m, + product: product, + forwardFn: _cloneNegative3.default + }); + return product; + } + + /** + * connects two matrices together by subtract + * @param {Matrix} left + * @param {Matrix} right + * @returns {Matrix} + */ + + }, { + key: 'subtract', + value: function subtract(left, right) { + if (left.weights.length !== right.weights.length) { + throw new Error('misaligned matrices'); + } + return this.add(this.add(this.allOnes(left.rows, left.columns), this.cloneNegative(left)), right); + } + + /** + * connects two matrices together by multiply + * @param {Matrix} left + * @param {Matrix} right + * @returns {Matrix} + */ + + }, { + key: 'multiply', + value: function multiply(left, right) { + if (left.columns !== right.rows) { + throw new Error('misaligned matrices'); + } + var product = new _2.default(left.rows, right.columns); + this.allMatrices.push(product); + this.states.push({ + left: left, + right: right, + product: product, + forwardFn: _multiply3.default, + backpropagationFn: _multiplyB2.default + }); + return product; + } + + /** + * connects two matrices together by multiplyElement + * @param {Matrix} left + * @param {Matrix} right + * @returns {Matrix} + */ + + }, { + key: 'multiplyElement', + value: function multiplyElement(left, right) { + if (left.weights.length !== right.weights.length) { + throw new Error('misaligned matrices'); + } + var product = new _2.default(left.rows, left.columns); + this.allMatrices.push(product); + this.states.push({ + left: left, + right: right, + product: product, + forwardFn: _multiplyElement3.default, + backpropagationFn: _multiplyElementB2.default + }); + return product; + } + + /** + * connects a matrix to relu + * @param {Matrix} m + * @returns {Matrix} + */ + + }, { + key: 'relu', + value: function relu(m) { + var product = new _2.default(m.rows, m.columns); + this.allMatrices.push(product); + this.states.push({ + left: m, + product: product, + forwardFn: _relu3.default, + backpropagationFn: _reluB2.default + }); + return product; + } + + /** + * connects a matrix via a row + * @param {Matrix} m + * @returns {Matrix} + */ + + }, { + key: 'inputMatrixToRow', + value: function inputMatrixToRow(m) { + var self = this; + var product = new _2.default(m.columns, 1); + this.allMatrices.push(product); + this.states.push({ + left: m, + get right() { + return self.inputRow; + }, + product: product, + forwardFn: _rowPluck2.default, + backpropagationFn: _rowPluckB2.default + }); + return product; + } + + /** + * connects a matrix to sigmoid + * @param {Matrix} m + * @returns {Matrix} + */ + + }, { + key: 'sigmoid', + value: function sigmoid(m) { + var product = new _2.default(m.rows, m.columns); + this.allMatrices.push(product); + this.states.push({ + left: m, + product: product, + forwardFn: _sigmoid3.default, + backpropagationFn: _sigmoidB2.default + }); + return product; + } + + /** + * connects a matrix to tanh + * @param {Matrix} m + * @returns {Matrix} + */ + + }, { + key: 'tanh', + value: function tanh(m) { + var product = new _2.default(m.rows, m.columns); + this.allMatrices.push(product); + this.states.push({ + left: m, + product: product, + forwardFn: _tanh3.default, + backpropagationFn: _tanhB2.default + }); + return product; + } + + /** + * + * @param m + * @returns {Matrix} + */ + + }, { + key: 'observe', + value: function observe(m) { + var iForward = 0; + var iBackpropagate = 0; + this.states.push({ + forwardFn: function forwardFn() { + iForward++; + }, + backpropagationFn: function backpropagationFn() { + iBackpropagate++; + } + }); + return m; + } + + /** + * @patam {Number} [rowIndex] + * @output {Matrix} + */ + + }, { + key: 'run', + value: function run() { + var rowIndex = arguments.length <= 0 || arguments[0] === undefined ? 0 : arguments[0]; + + this.inputRow = rowIndex; + var state = void 0; + for (var i = 0, max = this.states.length; i < max; i++) { + state = this.states[i]; + if (!state.hasOwnProperty('forwardFn')) { + continue; + } + state.forwardFn(state.product, state.left, state.right); + } + + return state.product; + } + + /** + * @patam {Number} [rowIndex] + * @output {Matrix} + */ + + }, { + key: 'runBackpropagate', + value: function runBackpropagate() { + var rowIndex = arguments.length <= 0 || arguments[0] === undefined ? 0 : arguments[0]; + + this.inputRow = rowIndex; + + var i = this.states.length; + var state = void 0; + while (i-- > 0) { + state = this.states[i]; + if (!state.hasOwnProperty('backpropagationFn')) { + continue; + } + state.backpropagationFn(state.product, state.left, state.right); + } + + return state.product; + } + }]); + + return Equation; +}(); + +exports.default = Equation; + +},{"./":13,"./add":8,"./add-b":7,"./all-ones":9,"./clone-negative":10,"./copy":11,"./multiply":18,"./multiply-b":15,"./multiply-element":17,"./multiply-element-b":16,"./ones-matrix":19,"./relu":22,"./relu-b":21,"./row-pluck":24,"./row-pluck-b":23,"./sigmoid":27,"./sigmoid-b":26,"./tanh":30,"./tanh-b":29}],13:[function(require,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; }; }(); + +var _zeros = require('../../utilities/zeros'); + +var _zeros2 = _interopRequireDefault(_zeros); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +/** + * A matrix + * @param {Number} [rows] + * @param {Number} [columns] + * @constructor + */ +var Matrix = function () { + function Matrix(rows, columns) { + _classCallCheck(this, Matrix); + + if (typeof rows === 'undefined') return; + if (typeof columns === 'undefined') return; + + this.rows = rows; + this.columns = columns; + this.weights = (0, _zeros2.default)(rows * columns); + this.recurrence = (0, _zeros2.default)(rows * columns); + } + + /** + * + * @param {Number} row + * @param {Number} col + * @returns {Float64Array|Array} + */ + + + _createClass(Matrix, [{ + key: 'getWeights', + value: function getWeights(row, col) { + // slow but careful accessor function + // we want row-major order + var ix = this.columns * row + col; + if (ix < 0 && ix >= this.weights.length) throw new Error('get accessor is skewed'); + return this.weights[ix]; + } + + /** + * + * @param {Number} row + * @param {Number} col + * @param v + * @returns {Matrix} + */ + + }, { + key: 'setWeight', + value: function setWeight(row, col, v) { + // slow but careful accessor function + var ix = this.columns * row + col; + if (ix < 0 && ix >= this.weights.length) throw new Error('set accessor is skewed'); + this.weights[ix] = v; + } + + /** + * + * @param {Number} row + * @param {Number} col + * @param v + * @returns {Matrix} + */ + + }, { + key: 'setRecurrence', + value: function setRecurrence(row, col, v) { + // slow but careful accessor function + var ix = this.columns * row + col; + if (ix < 0 && ix >= this.weights.length) throw new Error('set accessor is skewed'); + this.recurrence[ix] = v; + } + + /** + * + * @returns {{rows: *, columns: *, weights: Array}} + */ + + }, { + key: 'toJSON', + value: function toJSON() { + return { + rows: this.rows, + columns: this.columns, + weights: this.weights.slice(0) + }; + } + }], [{ + key: 'fromJSON', + value: function fromJSON(json) { + var matrix = new Matrix(json.rows, json.columns); + for (var i = 0, max = json.rows * json.columns; i < max; i++) { + matrix.weights[i] = json.weights[i]; // copy over weights + } + return matrix; + } + + /** + * + * @param weightRows + * @param [recurrenceRows] + * @returns {Matrix} + */ + + }, { + key: 'fromArray', + value: function fromArray(weightRows, recurrenceRows) { + var rows = weightRows.length; + var columns = weightRows[0].length; + var m = new Matrix(rows, columns); + + recurrenceRows = recurrenceRows || weightRows; + + for (var rowIndex = 0; rowIndex < rows; rowIndex++) { + var weightValues = weightRows[rowIndex]; + var recurrentValues = recurrenceRows[rowIndex]; + for (var columnIndex = 0; columnIndex < columns; columnIndex++) { + m.setWeight(rowIndex, columnIndex, weightValues[columnIndex]); + m.setRecurrence(rowIndex, columnIndex, recurrentValues[columnIndex]); + } + } + + return m; + } + }]); + + return Matrix; +}(); + +exports.default = Matrix; + +},{"../../utilities/zeros":42}],14:[function(require,module,exports){ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = maxI; +/** + * + * @param {Matrix} m + * @returns {number} + */ +function maxI(m) { + // argmax of array w + var w = m.weights; + var maxv = w[0]; + var maxix = 0; + for (var i = 1, max = w.length; i < max; i++) { + var v = w[i]; + if (v < maxv) continue; + + maxix = i; + maxv = v; + } + return maxix; +}; + +},{}],15:[function(require,module,exports){ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = multiplyB; +/** + * multiplies {from} recurrence to {left} and {right} + * @param {Matrix} product + * @param {Matrix} left + * @param {Matrix} right + */ +function multiplyB(product, left, right) { + var leftRows = left.rows; + var leftColumns = left.columns; + var rightColumns = right.columns; + + // loop over rows of left + for (var leftRow = 0; leftRow < leftRows; leftRow++) { + + // loop over cols of right + for (var rightColumn = 0; rightColumn < rightColumns; rightColumn++) { + + //loop over columns of left + for (var leftColumn = 0; leftColumn < leftColumns; leftColumn++) { + var backPropagateValue = product.recurrence[rightColumns * leftRow + rightColumn]; + left.recurrence[leftColumns * leftRow + leftColumn] += right.weights[rightColumns * leftColumn + rightColumn] * backPropagateValue; + right.recurrence[rightColumns * leftColumn + rightColumn] += left.weights[leftColumns * leftRow + leftColumn] * backPropagateValue; + } + } + } +} + +},{}],16:[function(require,module,exports){ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = multiplyElementB; +/** + * multiplies {left} and {right} weight by {from} recurrence into {left} and {right} recurrence + * @param {Matrix} product + * @param {Matrix} left + * @param {Matrix} right + */ +function multiplyElementB(product, left, right) { + for (var i = 0, weights = left.weights.length; i < weights; i++) { + left.recurrence[i] += right.weights[i] * product.recurrence[i]; + right.recurrence[i] += left.weights[i] * product.recurrence[i]; + } +} + +},{}],17:[function(require,module,exports){ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = multiplyElement; +/** + * @param {Matrix} product + * @param {Matrix} left + * @param {Matrix} right + */ +function multiplyElement(product, left, right) { + for (var i = 0, weights = left.weights.length; i < weights; i++) { + product.weights[i] = left.weights[i] * right.weights[i]; + } +} + +},{}],18:[function(require,module,exports){ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = multiply; +/** + * multiply {left} and {right} matrix weights to {into} + * @param {Matrix} product + * @param {Matrix} left + * @param {Matrix} right + */ +function multiply(product, left, right) { + var leftRows = left.rows; + var leftColumns = left.columns; + var rightColumns = right.columns; + + // loop over rows of left + for (var leftRow = 0; leftRow < leftRows; leftRow++) { + + // loop over cols of right + for (var rightColumn = 0; rightColumn < rightColumns; rightColumn++) { + + // dot product loop + var dot = 0; + + //loop over columns of left + for (var leftColumn = 0; leftColumn < leftColumns; leftColumn++) { + dot += left.weights[leftColumns * leftRow + leftColumn] * right.weights[rightColumns * leftColumn + rightColumn]; + } + var i = rightColumns * leftRow + rightColumn; + product.weights[i] = dot; + } + } +} + +},{}],19:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _ = require('./'); + +var _2 = _interopRequireDefault(_); + +var _ones = require('../../utilities/ones'); + +var _ones2 = _interopRequireDefault(_ones); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +/** return Matrix but filled with random numbers from gaussian + * @param {Number} [rows] + * @param {Number} [columns] + * @constructor + */ +var OnesMatrix = function (_Matrix) { + _inherits(OnesMatrix, _Matrix); + + function OnesMatrix(rows, columns) { + _classCallCheck(this, OnesMatrix); + + var _this = _possibleConstructorReturn(this, (OnesMatrix.__proto__ || Object.getPrototypeOf(OnesMatrix)).call(this, rows, columns)); + + _this.rows = rows; + _this.columns = columns; + _this.weights = (0, _ones2.default)(rows * columns); + _this.recurrence = (0, _ones2.default)(rows * columns); + return _this; + } + + return OnesMatrix; +}(_2.default); + +exports.default = OnesMatrix; + +},{"../../utilities/ones":35,"./":13}],20:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _ = require('./'); + +var _2 = _interopRequireDefault(_); + +var _random = require('../../utilities/random'); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +/** return Matrix but filled with random numbers from gaussian + * @param {Number} [rows] + * @param {Number} [columns] + * @param std + * @constructor + */ +var RandomMatrix = function (_Matrix) { + _inherits(RandomMatrix, _Matrix); + + function RandomMatrix(rows, columns, std) { + _classCallCheck(this, RandomMatrix); + + var _this = _possibleConstructorReturn(this, (RandomMatrix.__proto__ || Object.getPrototypeOf(RandomMatrix)).call(this, rows, columns)); + + _this.rows = rows; + _this.columns = columns; + _this.std = std; + for (var i = 0, max = _this.weights.length; i < max; i++) { + _this.weights[i] = (0, _random.randomF)(-std, std); + } + return _this; + } + + return RandomMatrix; +}(_2.default); + +exports.default = RandomMatrix; + +},{"../../utilities/random":37,"./":13}],21:[function(require,module,exports){ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = reluB; +/** + * adds {from} recurrence to {m} recurrence when {m} weights are above other a threshold of 0 + * @param {Matrix} product + * @param {Matrix} m + */ +function reluB(product, left) { + for (var i = 0, max = product.recurrence.length; i < max; i++) { + left.recurrence[i] += left.weights[i] > 0 ? product.recurrence[i] : 0; + } +} + +},{}],22:[function(require,module,exports){ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = relu; +/** + * + * relu {m} weights to {into} weights + * @param {Matrix} product + * @param {Matrix} left + */ +function relu(product, left) { + for (var i = 0, max = left.weights.length; i < max; i++) { + product.weights[i] = Math.max(0, left.weights[i]); // relu + } +} + +},{}],23:[function(require,module,exports){ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = rowPluckB; +/** + * adds {from} recurrence into {m} recurrence + * @param {Matrix} product + * @param {Matrix} left + * @param {Number} rowIndex + */ +function rowPluckB(product, left, rowIndex) { + for (var column = 0, columns = left.columns; column < columns; column++) { + left.recurrence[columns * rowIndex + column] += product.recurrence[column]; + } +} + +},{}],24:[function(require,module,exports){ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = rowPluck; +/** + * @param {Matrix} product + * @param {Matrix} left + * @param {Number} rowPluckIndex + */ +function rowPluck(product, left, rowPluckIndex) { + for (var column = 0, columns = left.columns; column < columns; column++) { + product.weights[column] = left.weights[columns * rowPluckIndex + column]; + } +} + +},{}],25:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = sampleI; + +var _random = require('../../utilities/random'); + +//prevent parser from renaming when calling toString() method later +var randomF = _random.randomF; +/** + * + * @param {Matrix} m + * @returns {number} + */ +function sampleI(m) { + // sample argmax from w, assuming w are + // probabilities that sum to one + var r = randomF(0, 1); + var x = 0; + var i = 0; + var w = m.weights; + + //TODO: Needed? + if (isNaN(w[0])) { + throw new Error('NaN'); + } + + while (true) { + x += w[i]; + if (x > r) { + return i; + } + i++; + } +} + +},{"../../utilities/random":37}],26:[function(require,module,exports){ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = sigmoidB; +/** + * + * @param {Matrix} product + * @param {Matrix} left + */ +function sigmoidB(product, left) { + for (var i = 0, max = product.recurrence.length; i < max; i++) { + var mwi = product.weights[i]; + left.recurrence[i] += mwi * (1 - mwi) * product.recurrence[i]; + } +} + +},{}],27:[function(require,module,exports){ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = sigmoid; +/** + * @param {Matrix} product + * @param {Matrix} left + */ +function sigmoid(product, left) { + // sigmoid nonlinearity + for (var i = 0, max = left.weights.length; i < max; i++) { + product.weights[i] = 1 / (1 + Math.exp(-left.weights[i])); + } +} + +function sig(x) { + // helper function for computing sigmoid + return 1 / (1 + Math.exp(-x)); +} + +},{}],28:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = softmax; + +var _ = require('./'); + +var _2 = _interopRequireDefault(_); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +//prevent parser from renaming when calling toString() method later +var Matrix = _2.default; +/** + * + * @param {Matrix} m + * @returns {Matrix} + */ +function softmax(m) { + var result = new Matrix(m.rows, m.columns); // probability volume + var maxVal = -999999; + var i = void 0; + var max = m.weights.length; + + for (i = 0; i < max; i++) { + if (m.weights[i] > maxVal) { + maxVal = m.weights[i]; + } + } + + var s = 0; + for (i = 0; i < max; i++) { + result.weights[i] = Math.exp(m.weights[i] - maxVal); + s += result.weights[i]; + } + + for (i = 0; i < max; i++) { + result.weights[i] /= s; + } + + // no backward pass here needed + // since we will use the computed probabilities outside + // to set gradients directly on m + return result; +} + +},{"./":13}],29:[function(require,module,exports){ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = tanhB; +/** + * + * @param {Matrix} product + * @param {Matrix} left + */ +function tanhB(product, left) { + for (var i = 0, max = product.recurrence.length; i < max; i++) { + // grad for z = tanh(x) is (1 - z^2) + var mwi = product.weights[i]; + left.recurrence[i] += (1 - mwi * mwi) * product.recurrence[i]; + } +} + +},{}],30:[function(require,module,exports){ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = tanh; +/** + * @param {Matrix} product + * @param {Matrix} left + */ +function tanh(product, left) { + // tanh nonlinearity + for (var i = 0, max = left.weights.length; i < max; i++) { + product.weights[i] = Math.tanh(left.weights[i]); + } +} + +},{}],31:[function(require,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; }; }(); + +var _matrix = require('./matrix'); + +var _matrix2 = _interopRequireDefault(_matrix); + +var _randomMatrix = require('./matrix/random-matrix'); + +var _randomMatrix2 = _interopRequireDefault(_randomMatrix); + +var _equation = require('./matrix/equation'); + +var _equation2 = _interopRequireDefault(_equation); + +var _sampleI2 = require('./matrix/sample-i'); + +var _sampleI3 = _interopRequireDefault(_sampleI2); + +var _maxI = require('./matrix/max-i'); + +var _maxI2 = _interopRequireDefault(_maxI); + +var _softmax = require('./matrix/softmax'); + +var _softmax2 = _interopRequireDefault(_softmax); + +var _copy = require('./matrix/copy'); + +var _copy2 = _interopRequireDefault(_copy); + +var _random = require('../utilities/random'); + +var _zeros = require('../utilities/zeros'); + +var _zeros2 = _interopRequireDefault(_zeros); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var defaults = { + isBackPropagate: true, + // hidden size should be a list + inputSize: 20, + inputRange: 20, + hiddenSizes: [20, 20], + outputSize: 20, + learningRate: 0.01, + decayRate: 0.999, + smoothEps: 1e-8, + regc: 0.000001, + clipval: 5, + json: null +}; + +var RNN = function () { + function RNN(options) { + _classCallCheck(this, RNN); + + options = options || {}; + + for (var p in defaults) { + if (defaults.hasOwnProperty(p) && p !== 'isBackPropagate') { + this[p] = options.hasOwnProperty(p) ? options[p] : defaults[p]; + } + } + + this.stepCache = {}; + this.runs = 0; + this.totalPerplexity = null; + this.totalCost = null; + this.ratioClipped = null; + + this.model = { + input: null, + hiddenLayers: [], + output: null, + equations: [], + allMatrices: [], + outputMatrixIndex: -1, + equationConnections: [] + }; + + if (this.json) { + this.fromJSON(this.json); + } else { + this.mapModel(); + } + } + + _createClass(RNN, [{ + key: 'createHiddenLayers', + value: function createHiddenLayers() { + var hiddenSizes = this.hiddenSizes; + var model = this.model; + var hiddenLayers = model.hiddenLayers; + //0 is end, so add 1 to offset + hiddenLayers.push(this.getModel(hiddenSizes[0], this.inputSize)); + var prevSize = hiddenSizes[0]; + + for (var d = 1; d < hiddenSizes.length; d++) { + // loop over depths + var hiddenSize = hiddenSizes[d]; + hiddenLayers.push(this.getModel(hiddenSize, prevSize)); + prevSize = hiddenSize; + } + } + + /** + * + * @param {Number} hiddenSize + * @param {Number} prevSize + * @returns {object} + */ + + }, { + key: 'getModel', + value: function getModel(hiddenSize, prevSize) { + return { + //wxh + weight: new _randomMatrix2.default(hiddenSize, prevSize, 0.08), + //whh + transition: new _randomMatrix2.default(hiddenSize, hiddenSize, 0.08), + //bhh + bias: new _matrix2.default(hiddenSize, 1) + }; + } + + /** + * + * @param {Equation} equation + * @param {Matrix} inputMatrix + * @param {Matrix} previousResult + * @param {Object} hiddenLayer + * @returns {Matrix} + */ + + }, { + key: 'getEquation', + value: function getEquation(equation, inputMatrix, previousResult, hiddenLayer) { + var relu = equation.relu.bind(equation); + var add = equation.add.bind(equation); + var multiply = equation.multiply.bind(equation); + + return relu(add(add(multiply(hiddenLayer.weight, inputMatrix), multiply(hiddenLayer.transition, previousResult)), hiddenLayer.bias)); + } + }, { + key: 'createInputMatrix', + value: function createInputMatrix() { + //0 is end, so add 1 to offset + this.model.input = new _randomMatrix2.default(this.inputRange + 1, this.inputSize, 0.08); + } + }, { + key: 'createOutputMatrix', + value: function createOutputMatrix() { + var model = this.model; + var outputSize = this.outputSize; + var lastHiddenSize = this.hiddenSizes[this.hiddenSizes.length - 1]; + + //0 is end, so add 1 to offset + //whd + model.outputConnector = new _randomMatrix2.default(outputSize + 1, lastHiddenSize, 0.08); + //0 is end, so add 1 to offset + //bd + model.output = new _matrix2.default(outputSize + 1, 1); + } + }, { + key: 'bindEquation', + value: function bindEquation() { + var model = this.model; + var hiddenSizes = this.hiddenSizes; + var hiddenLayers = model.hiddenLayers; + var equation = new _equation2.default(); + var outputs = []; + var equationConnection = model.equationConnections.length > 0 ? model.equationConnections[model.equationConnections.length - 1] : hiddenSizes.map(function (size) { + return new _matrix2.default(hiddenSizes[0], 1); + }); + + // 0 index + var output = this.getEquation(equation, equation.inputMatrixToRow(model.input), equationConnection[0], hiddenLayers[0]); + outputs.push(output); + // 1+ indexes + for (var i = 1, max = hiddenSizes.length; i < max; i++) { + output = this.getEquation(equation, output, equationConnection[i], hiddenLayers[i]); + outputs.push(output); + } + + model.equationConnections.push(outputs); + equation.add(equation.multiply(model.outputConnector, output), model.output); + model.allMatrices = model.allMatrices.concat(equation.allMatrices); + model.equations.push(equation); + } + }, { + key: 'mapModel', + value: function mapModel() { + var model = this.model; + var hiddenLayers = model.hiddenLayers; + var allMatrices = model.allMatrices; + + this.createInputMatrix(); + if (!model.input) throw new Error('net.model.input not set'); + allMatrices.push(model.input); + + this.createHiddenLayers(); + if (!model.hiddenLayers.length) throw new Error('net.hiddenLayers not set'); + for (var i = 0, max = hiddenLayers.length; i < max; i++) { + var hiddenMatrix = hiddenLayers[i]; + for (var property in hiddenMatrix) { + if (!hiddenMatrix.hasOwnProperty(property)) continue; + allMatrices.push(hiddenMatrix[property]); + } + } + + this.createOutputMatrix(); + if (!model.outputConnector) throw new Error('net.model.outputConnector not set'); + if (!model.output) throw new Error('net.model.output not set'); + + allMatrices.push(model.outputConnector); + model.outputMatrixIndex = allMatrices.length; + allMatrices.push(model.output); + } + }, { + key: 'run', + value: function run(input) { + this.train(input); + this.runBackpropagate(input); + this.step(); + } + }, { + key: 'train', + value: function train(input) { + this.runs++; + var model = this.model; + var max = input.length; + var log2ppl = 0; + var cost = 0; + + var equation = void 0; + while (model.equations.length <= input.length + 1) { + //first and last are zeros + this.bindEquation(); + } + for (var inputIndex = -1, inputMax = input.length; inputIndex < inputMax; inputIndex++) { + // start and end tokens are zeros + equation = model.equations[inputIndex + 1]; + + var source = inputIndex === -1 ? 0 : input[inputIndex] + 1; // first step: start with START token + var target = inputIndex === max - 1 ? 0 : input[inputIndex + 1] + 1; // last step: end with END token + var output = equation.run(source); + // set gradients into log probabilities + var logProbabilities = output; // interpret output as log probabilities + var probabilities = (0, _softmax2.default)(output); // compute the softmax probabilities + + log2ppl += -Math.log2(probabilities.weights[target]); // accumulate base 2 log prob and do smoothing + cost += -Math.log(probabilities.weights[target]); + + // write gradients into log probabilities + logProbabilities.recurrence = probabilities.weights; + logProbabilities.recurrence[target] -= 1; + } + + this.totalPerplexity = Math.pow(2, log2ppl / (max - 1)); + this.totalCost = cost; + } + }, { + key: 'runBackpropagate', + value: function runBackpropagate(input) { + var i = input.length + 0; + var model = this.model; + var equations = model.equations; + while (i > 0) { + equations[i].runBackpropagate(input[i - 1] + 1); + i--; + } + equations[0].runBackpropagate(0); + } + }, { + key: 'step', + value: function step() { + // perform parameter update + var stepSize = this.learningRate; + var regc = this.regc; + var clipval = this.clipval; + var model = this.model; + var numClipped = 0; + var numTot = 0; + var allMatrices = model.allMatrices; + var matrixIndexes = allMatrices.length; + for (var matrixIndex = 0; matrixIndex < matrixIndexes; matrixIndex++) { + var matrix = allMatrices[matrixIndex]; + if (!(matrixIndex in this.stepCache)) { + this.stepCache[matrixIndex] = new _matrix2.default(matrix.rows, matrix.columns); + } + var cache = this.stepCache[matrixIndex]; + + //if we are in an equation, reset the weights and recurrence to 0, to prevent exploding gradient problem + if (matrixIndex > model.outputMatrixIndex) { + for (var i = 0, n = matrix.weights.length; i < n; i++) { + matrix.weights[i] = 0; + matrix.recurrence[i] = 0; + } + continue; + } + + for (var _i = 0, _n = matrix.weights.length; _i < _n; _i++) { + // rmsprop adaptive learning rate + var mdwi = matrix.recurrence[_i]; + cache.weights[_i] = cache.weights[_i] * this.decayRate + (1 - this.decayRate) * mdwi * mdwi; + // gradient clip + if (mdwi > clipval) { + mdwi = clipval; + numClipped++; + } + if (mdwi < -clipval) { + mdwi = -clipval; + numClipped++; + } + numTot++; + + // update (and regularize) + matrix.weights[_i] = matrix.weights[_i] + -stepSize * mdwi / Math.sqrt(cache.weights[_i] + this.smoothEps) - regc * matrix.weights[_i]; + matrix.recurrence[_i] = 0; // reset gradients for next iteration + } + } + this.ratioClipped = numClipped / numTot; + } + }, { + key: 'predict', + value: function predict() { + var result = arguments.length <= 0 || arguments[0] === undefined ? [] : arguments[0]; + var maxPredictionLength = arguments.length <= 1 || arguments[1] === undefined ? 100 : arguments[1]; + + var _sampleI = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2]; + + var temperature = arguments.length <= 3 || arguments[3] === undefined ? 1 : arguments[3]; + + var model = this.model; + var equation = void 0; + var i = 0; + while (model.equations.length < maxPredictionLength) { + this.bindEquation(); + } + while (true) { + var ix = result.length === 0 ? 0 : result[result.length - 1]; + equation = model.equations[i]; + // sample predicted letter + var output = equation.run(ix); + + var logProbabilities = new _matrix2.default(model.output.rows, model.output.columns); + (0, _copy2.default)(logProbabilities, output); + if (temperature !== 1 && _sampleI) { + // scale log probabilities by temperature and renormalize + // if temperature is high, logprobs will go towards zero + // and the softmax outputs will be more diffuse. if temperature is + // very low, the softmax outputs will be more peaky + for (var q = 0, nq = logProbabilities.weights.length; q < nq; q++) { + logProbabilities.weights[q] /= temperature; + } + } + + var probs = (0, _softmax2.default)(logProbabilities); + + if (_sampleI) { + ix = (0, _sampleI3.default)(probs); + } else { + ix = (0, _maxI2.default)(probs); + } + + i++; + if (ix === 0) { + // END token predicted, break out + break; + } + if (i >= maxPredictionLength) { + // something is wrong + break; + } + + result.push(ix); + } + + return result.map(function (value) { + return value - 1; + }); + } + + /** + * + * @param input + * @returns {*} + */ + + }, { + key: 'runInput', + value: function runInput(input) { + this.outputs[0] = input; // set output state of input layer + + var output = null; + for (var layer = 1; layer <= this.outputLayer; layer++) { + for (var node = 0; node < this.sizes[layer]; node++) { + var weights = this.weights[layer][node]; + + var sum = this.biases[layer][node]; + for (var k = 0; k < weights.length; k++) { + sum += weights[k] * input[k]; + } + this.outputs[layer][node] = 1 / (1 + Math.exp(-sum)); + } + output = input = this.outputs[layer]; + } + return output; + } + + /** + * + * @param data + * @param options + * @returns {{error: number, iterations: number}} + */ + /*train(data, options) { + throw new Error('not yet implemented'); + //data = this.formatData(data); + options = options || {}; + let iterations = options.iterations || 20000; + let errorThresh = options.errorThresh || 0.005; + let log = options.log ? (typeof options.log === 'function' ? options.log : console.log) : false; + let logPeriod = options.logPeriod || 10; + let learningRate = options.learningRate || this.learningRate || 0.3; + let callback = options.callback; + let callbackPeriod = options.callbackPeriod || 10; + let sizes = []; + let inputSize = data[0].input.length; + let outputSize = data[0].output.length; + let hiddenSizes = this.hiddenSizes; + if (!hiddenSizes) { + sizes.push(Math.max(3, Math.floor(inputSize / 2))); + } else { + hiddenSizes.forEach(function(size) { + sizes.push(size); + }); + } + sizes.unshift(inputSize); + sizes.push(outputSize); + //this.initialize(sizes, options.keepNetworkIntact); + let error = 1; + for (let i = 0; i < iterations && error > errorThresh; i++) { + let sum = 0; + for (let j = 0; j < data.length; j++) { + let err = this.trainPattern(data[j].input, data[j].output, learningRate); + sum += err; + } + error = sum / data.length; + if (log && (i % logPeriod == 0)) { + log('iterations:', i, 'training error:', error); + } + if (callback && (i % callbackPeriod == 0)) { + callback({ error: error, iterations: i }); + } + } + return { + error: error, + iterations: i + }; + }*/ + + /** + * + * @param input + * @param target + * @param learningRate + */ + + }, { + key: 'trainPattern', + value: function trainPattern(input, target, learningRate) { + throw new Error('not yet implemented'); + } + + /** + * + * @param target + */ + + }, { + key: 'calculateDeltas', + value: function calculateDeltas(target) { + throw new Error('not yet implemented'); + } + + /** + * + * @param learningRate + */ + + }, { + key: 'adjustWeights', + value: function adjustWeights(learningRate) { + throw new Error('not yet implemented'); + } + + /** + * + * @param data + * @returns {*} + */ + + }, { + key: 'formatData', + value: function formatData(data) { + throw new Error('not yet implemented'); } /** * - * @returns + * @param data + * @returns { * { - * layers: [ - * { - * x: {}, - * y: {} - * }, - * { - * '0': { - * bias: -0.98771313, - * weights: { - * x: 0.8374838, - * y: 1.245858 - * }, - * '1': { - * bias: 3.48192004, - * weights: { - * x: 1.7825821, - * y: -2.67899 - * } - * } - * }, - * { - * f: { - * bias: 0.27205739, - * weights: { - * '0': 1.3161821, - * '1': 2.00436 - * } - * } - * } - * ] + * error: number, + * misclasses: Array * } + * } */ + }, { + key: 'test', + value: function test(data) { + throw new Error('not yet implemented'); + } }, { key: 'toJSON', value: function toJSON() { - var layers = []; - for (var layer = 0; layer <= this.outputLayer; layer++) { - layers[layer] = {}; + var model = this.model; + var options = {}; + for (var p in defaults) { + options[p] = this[p]; + } - var nodes = void 0; - // turn any internal arrays back into hashes for readable json - if (layer == 0 && this.inputLookup) { - nodes = Object.keys(this.inputLookup); - } else if (layer == this.outputLayer && this.outputLookup) { - nodes = Object.keys(this.outputLookup); - } else { - nodes = (0, _range2.default)(0, this.sizes[layer]); + return { + type: this.constructor.name, + options: options, + input: model.input.toJSON(), + hiddenLayers: model.hiddenLayers.map(function (hiddenLayer) { + var layers = {}; + for (var _p in hiddenLayer) { + layers[_p] = hiddenLayer[_p].toJSON(); + } + return layers; + }), + outputConnector: this.model.outputConnector.toJSON(), + output: this.model.output.toJSON() + }; + } + }, { + key: 'fromJSON', + value: function fromJSON(json) { + this.json = json; + var model = this.model; + var options = json.options; + var allMatrices = model.allMatrices; + model.input = _matrix2.default.fromJSON(json.input); + allMatrices.push(model.input); + model.hiddenLayers = json.hiddenLayers.map(function (hiddenLayer) { + var layers = {}; + for (var p in hiddenLayer) { + layers[p] = _matrix2.default.fromJSON(hiddenLayer[p]); + allMatrices.push(layers[p]); } + return layers; + }); + model.outputConnector = _matrix2.default.fromJSON(json.outputConnector); + model.output = _matrix2.default.fromJSON(json.output); + allMatrices.push(model.outputConnector, model.output); - for (var j = 0; j < nodes.length; j++) { - var node = nodes[j]; - layers[layer][node] = {}; - - if (layer > 0) { - layers[layer][node].bias = this.biases[layer][j]; - layers[layer][node].weights = {}; - for (var k in layers[layer - 1]) { - var index = k; - if (layer == 1 && this.inputLookup) { - index = this.inputLookup[k]; - } - layers[layer][node].weights[k] = this.weights[layer][j][index]; - } - } + for (var p in defaults) { + if (defaults.hasOwnProperty(p) && p !== 'isBackPropagate') { + this[p] = options.hasOwnProperty(p) ? options[p] : defaults[p]; } } - return { layers: layers, outputLookup: !!this.outputLookup, inputLookup: !!this.inputLookup }; + + this.bindEquation(); } /** * - * @param json - * @returns {NeuralNetwork} + * @returns {Function} */ }, { - key: 'fromJSON', - value: function fromJSON(json) { - var size = json.layers.length; - this.outputLayer = size - 1; + key: 'toFunction', + value: function toFunction() { + var model = this.model; + var equations = this.model.equations; + var equation = equations[1]; + var states = equation.states; + var modelAsString = JSON.stringify(this.toJSON()); + + function matrixOrigin(m, stateIndex) { + for (var i = 0, max = states.length; i < max; i++) { + var state = states[i]; + + if (i === stateIndex) { + var j = previousConnectionIndex(m); + switch (m) { + case state.left: + if (j > -1) { + return 'typeof prevStates[' + j + '] === \'object\' ? prevStates[' + j + '].product : new Matrix(' + m.rows + ', ' + m.columns + ')'; + } + case state.right: + if (j > -1) { + return 'typeof prevStates[' + j + '] === \'object\' ? prevStates[' + j + '].product : new Matrix(' + m.rows + ', ' + m.columns + ')'; + } + case state.product: + return 'new Matrix(' + m.rows + ', ' + m.columns + ')'; + default: + throw Error('unknown state'); + } + } - this.sizes = new Array(size); - this.weights = new Array(size); - this.biases = new Array(size); - this.outputs = new Array(size); + if (m === state.product) return 'states[' + i + '].product'; + if (m === state.right) return 'states[' + i + '].right'; + if (m === state.left) return 'states[' + i + '].left'; + } + } - for (var i = 0; i <= this.outputLayer; i++) { - var layer = json.layers[i]; - if (i == 0 && (!layer[0] || json.inputLookup)) { - this.inputLookup = _lookup2.default.lookupFromHash(layer); - } else if (i == this.outputLayer && (!layer[0] || json.outputLookup)) { - this.outputLookup = _lookup2.default.lookupFromHash(layer); + function previousConnectionIndex(m) { + var connection = model.equationConnections[0]; + var states = equations[0].states; + for (var i = 0, max = states.length; i < max; i++) { + if (states[i].product === m) { + return i; + } } + return connection.indexOf(m); + } - var nodes = Object.keys(layer); - this.sizes[i] = nodes.length; - this.weights[i] = []; - this.biases[i] = []; - this.outputs[i] = []; + function matrixToString(m, stateIndex) { + if (!m || !m.rows || !m.columns) return 'null'; - for (var j in nodes) { - var node = nodes[j]; - this.biases[i][j] = layer[node].bias; - this.weights[i][j] = (0, _toArray2.default)(layer[node].weights); + if (m === model.input) return 'model.input'; + if (m === model.outputConnector) return 'model.outputConnector'; + if (m === model.output) return 'model.output'; + + for (var i = 0, max = model.hiddenLayers.length; i < max; i++) { + var hiddenLayer = model.hiddenLayers[i]; + for (var p in hiddenLayer) { + if (!hiddenLayer.hasOwnProperty(p)) continue; + if (hiddenLayer[p] !== m) continue; + return 'model.hiddenLayers[' + i + '].' + p; + } } + + return matrixOrigin(m, stateIndex); } - return this; - } - /** - * - * @returns {Function} - */ + function toInner(fnString) { + //crude, but should be sufficient for now + // function() { body } + fnString = fnString.toString().split('{'); + fnString.shift(); + // body } + fnString = fnString.join('{'); + fnString = fnString.split('}'); + fnString.pop(); + // body + return fnString.join('}').split('\n').join('\n '); + } - }, { - key: 'toFunction', - value: function toFunction() { - var json = this.toJSON(); - var jsonString = JSON.stringify(json); - // return standalone function that mimics run() - return new Function('input', '\n var net = ' + jsonString + ';\n for (var i = 1; i < net.layers.length; i++) {\n var layer = net.layers[i];\n var output = {};\n \n for (var id in layer) {\n var node = layer[id];\n var sum = node.bias;\n \n for (var iid in node.weights) {\n sum += node.weights[iid] * input[iid];\n }\n output[id] = (1 / (1 + Math.exp(-sum)));\n }\n input = output;\n }\n return output;\n '); - } + function fileName(fnName) { + return 'src/recurrent/matrix/' + fnName.replace(/[A-Z]/g, function (value) { + return '-' + value.toLowerCase(); + }) + '.js'; + } - /** - * This will create a TrainStream (WriteStream) for us to send the training data to. - * @param opts training options - * @returns {TrainStream|*} - */ + var statesRaw = []; + var usedFunctionNames = {}; + var innerFunctionsSwitch = []; + for (var i = 0, max = states.length; i < max; i++) { + var state = states[i]; + statesRaw.push('states[' + i + '] = {\n name: \'' + state.forwardFn.name + '\',\n left: ' + matrixToString(state.left, i) + ',\n right: ' + matrixToString(state.right, i) + ',\n product: ' + matrixToString(state.product, i) + '\n }'); + + var fnName = state.forwardFn.name; + if (!usedFunctionNames[fnName]) { + usedFunctionNames[fnName] = true; + innerFunctionsSwitch.push(' case \'' + fnName + '\': //compiled from ' + fileName(fnName) + '\n ' + toInner(state.forwardFn.toString()) + '\n break;'); + } + } - }, { - key: 'createTrainStream', - value: function createTrainStream(opts) { - opts = opts || {}; - opts.neuralNetwork = this; - this.trainStream = new _trainStream2.default(opts); - return this.trainStream; + return new Function('input', 'maxPredictionLength', '_sampleI', 'temperature', '\n if (typeof input === \'undefined\') input = [];\n if (typeof maxPredictionLength === \'undefined\') maxPredictionLength = 100;\n if (typeof _sampleI === \'undefined\') _sampleI = false;\n if (typeof temperature === \'undefined\') temperature = 1;\n \n var model = ' + modelAsString + ';\n var _i = 0;\n var result = input.slice(0);\n var states = [];\n var prevStates;\n while (true) {\n // sample predicted letter\n var ix = result.length === 0 ? 0 : result[result.length - 1]; // first step: start with START token\n var rowPluckIndex = ix; //connect up to rowPluck\n prevStates = states;\n states = [];\n ' + statesRaw.join(';\n ') + ';\n for (var stateIndex = 0, stateMax = ' + statesRaw.length + '; stateIndex < stateMax; stateIndex++) {\n var state = states[stateIndex];\n var product = state.product;\n var left = state.left;\n var right = state.right;\n \n switch (state.name) {\n' + innerFunctionsSwitch.join('\n') + '\n }\n }\n \n var logProbabilities = state.product;\n if (temperature !== 1 && _sampleI) {\n // scale log probabilities by temperature and renormalize\n // if temperature is high, logprobs will go towards zero\n // and the softmax outputs will be more diffuse. if temperature is\n // very low, the softmax outputs will be more peaky\n for (var q = 0, nq = logProbabilities.weights.length; q < nq; q++) {\n logProbabilities.weights[q] /= temperature;\n }\n }\n\n var probs = softmax(logProbabilities);\n\n if (_sampleI) {\n ix = sampleI(probs);\n } else {\n ix = maxI(probs);\n }\n \n _i++;\n if (ix === 0) {\n // END token predicted, break out\n break;\n }\n if (_i >= maxPredictionLength) {\n // something is wrong\n break;\n }\n\n result.push(ix);\n }\n\n return result.map(function(value) { return value - 1; });\n \n function Matrix(rows, columns) {\n this.rows = rows;\n this.columns = columns;\n this.weights = zeros(rows * columns);\n this.recurrence = zeros(rows * columns);\n }\n ' + _zeros2.default.toString() + '\n ' + _softmax2.default.toString() + '\n ' + _random.randomF.toString() + '\n ' + _sampleI3.default.toString() + '\n ' + _maxI2.default.toString()); } }]); - return NeuralNetwork; + return RNN; }(); -exports.default = NeuralNetwork; - +exports.default = RNN; -},{"./lookup":5,"./train-stream":7,"./utilities/max":8,"./utilities/mse":9,"./utilities/randos":11,"./utilities/range":12,"./utilities/to-array":13,"./utilities/zeros":14}],7:[function(require,module,exports){ +},{"../utilities/random":37,"../utilities/zeros":42,"./matrix":13,"./matrix/copy":11,"./matrix/equation":12,"./matrix/max-i":14,"./matrix/random-matrix":20,"./matrix/sample-i":25,"./matrix/softmax":28}],32:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { @@ -1234,8 +3278,7 @@ function uniques(arr) { return a; } - -},{"./lookup":5,"stream":38}],8:[function(require,module,exports){ +},{"./lookup":3,"stream":67}],33:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { @@ -1258,8 +3301,7 @@ function max(values) { return Math.max.apply(Math, (0, _toArray2.default)(values)); } - -},{"./to-array":13}],9:[function(require,module,exports){ +},{"./to-array":40}],34:[function(require,module,exports){ "use strict"; Object.defineProperty(exports, "__esModule", { @@ -1275,8 +3317,23 @@ function mse(errors) { return sum / errors.length; } +},{}],35:[function(require,module,exports){ +'use strict'; -},{}],10:[function(require,module,exports){ +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = ones; +function ones(size) { + if (typeof Float64Array !== 'undefined') return new Float64Array(size).fill(1); + var array = new Array(size); + for (var i = 0; i < size; i++) { + array[i] = i; + } + return array; +} + +},{}],36:[function(require,module,exports){ "use strict"; Object.defineProperty(exports, "__esModule", { @@ -1287,8 +3344,48 @@ function randomWeight() { return Math.random() * 0.4 - 0.2; } +},{}],37:[function(require,module,exports){ +"use strict"; -},{}],11:[function(require,module,exports){ +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.randomF = randomF; +exports.randomI = randomI; +exports.randomN = randomN; +function randomF(a, b) { + return Math.random() * (b - a) + a; +} + +function randomI(a, b) { + return Math.floor(Math.random() * (b - a) + a); +} + +function randomN(mu, std) { + return mu + gaussRandom() * std; +} + +// Random numbers utils +function gaussRandom() { + if (gaussRandom.returnV) { + gaussRandom.returnV = false; + return gaussRandom.vVal; + } + var u = 2 * Math.random() - 1; + var v = 2 * Math.random() - 1; + var r = u * u + v * v; + if (r == 0 || r > 1) { + return gaussRandom(); + } + var c = Math.sqrt(-2 * Math.log(r) / r); + gaussRandom.vVal = v * c; // cache this + gaussRandom.returnV = true; + return u * c; +} +gaussRandom.returnV = false; +gaussRandom.vVal = 0; + +},{}],38:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { @@ -1310,8 +3407,7 @@ function randos(size) { return array; } - -},{"./random-weight":10}],12:[function(require,module,exports){ +},{"./random-weight":36}],39:[function(require,module,exports){ "use strict"; Object.defineProperty(exports, "__esModule", { @@ -1332,8 +3428,7 @@ function range(start, end) { return result; } - -},{}],13:[function(require,module,exports){ +},{}],40:[function(require,module,exports){ "use strict"; Object.defineProperty(exports, "__esModule", { @@ -1356,15 +3451,136 @@ function toArray(values) { } } +},{}],41:[function(require,module,exports){ +'use strict'; -},{}],14:[function(require,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 _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +/** + * + * @param {String[]|Number[]} values + * @param maxThreshold + * @constructor + */ +var Vocab = function () { + function Vocab(values) { + var maxThreshold = arguments.length <= 1 || arguments[1] === undefined ? 0 : arguments[1]; + + _classCallCheck(this, Vocab); + + this.values = values; + // go over all characters and keep track of all unique ones seen + // count up all characters + this.indexTable = {}; + this.characterTable = {}; + this.characters = []; + var tempCharactersTable = {}; + for (var vocabIndex = 0, vocabLength = values.length; vocabIndex < vocabLength; vocabIndex++) { + var characters = values[vocabIndex].toString(); + for (var characterIndex = 0, _charactersLength = characters.length; characterIndex < _charactersLength; characterIndex++) { + var character = characters[characterIndex]; + if (character in tempCharactersTable) continue; + tempCharactersTable[character] = true; + this.characters.push(character); + } + } + + // filter by count threshold and create pointers + + // NOTE: start at one because we will have START and END tokens! + // that is, START token will be index 0 in model letter vectors + // and END token will be index 0 in the next character softmax + var charactersLength = this.characters.length; + for (var _characterIndex = 0; _characterIndex < charactersLength; _characterIndex++) { + var _character = this.characters[_characterIndex]; + if (_characterIndex >= maxThreshold) { + // add character to vocab + this.indexTable[_character] = _characterIndex; + this.characterTable[_characterIndex] = _character; + } + } + } + + _createClass(Vocab, [{ + key: 'toIndexes', + value: function toIndexes(phrase) { + var maxThreshold = arguments.length <= 1 || arguments[1] === undefined ? 0 : arguments[1]; + + var result = []; + var indexTable = this.indexTable; + + for (var i = 0, max = phrase.length; i < max; i++) { + var character = phrase[i]; + var index = indexTable[character]; + if (index < maxThreshold) continue; + result.push(index); + } + + return result; + } + }, { + key: 'toCharacters', + value: function toCharacters(indexes) { + var maxThreshold = arguments.length <= 1 || arguments[1] === undefined ? 0 : arguments[1]; + + var result = []; + var characterTable = this.characterTable; + + for (var i = 0, max = indexes.length; i < max; i++) { + var index = indexes[i]; + if (index < maxThreshold) continue; + var character = characterTable[index]; + result.push(character); + } + + return result; + } + }, { + key: 'toString', + value: function toString(indexes, maxThreshold) { + return this.toCharacters(indexes, maxThreshold).join(''); + } + }], [{ + key: 'allPrintable', + value: function allPrintable(maxThreshold) { + var values = []; + for (var i = 32; i <= 126; i++) { + values.push(String.fromCharCode(i)); + } + return new Vocab(values, maxThreshold); + } + }, { + key: 'fromString', + value: function fromString(string, maxThreshold) { + var _String$prototype; + + var values = (_String$prototype = String.prototype).concat.apply(_String$prototype, _toConsumableArray(new Set(string))); + return new Vocab(values, maxThreshold); + } + }]); + + return Vocab; +}(); + +exports.default = Vocab; + +},{}],42:[function(require,module,exports){ +'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = zeros; function zeros(size) { + if (typeof Float64Array !== 'undefined') return new Float64Array(size); var array = new Array(size); for (var i = 0; i < size; i++) { array[i] = 0; @@ -1372,8 +3588,58 @@ function zeros(size) { return array; } +},{}],43:[function(require,module,exports){ +var crossValidate = require('./dist/cross-validate'); +var likely = require('./dist/likely'); +var lookup = require('./dist/lookup'); +var NeuralNetwork = require('./dist/neural-network'); +var TrainStream = require('./dist/train-stream'); +var RNN = require('./dist/recurrent/rnn'); +var LSTM = require('./dist/recurrent/lstm'); +var GRU = require('./dist/recurrent/gru'); +var utilities = { + max: require('./dist/utilities/max'), + mse: require('./dist/utilities/mse'), + ones: require('./dist/utilities/ones'), + random: require('./dist/utilities/random'), + randomWeight: require('./dist/utilities/random-weight'), + randos: require('./dist/utilities/randos'), + range: require('./dist/utilities/range'), + toArray: require('./dist/utilities/to-array'), + Vocab: require('./dist/utilities/vocab'), + zeros: require('./dist/utilities/zeros') +}; + +module.exports = { + crossValidate: crossValidate, + likely: likely, + lookup: lookup, + NeuralNetwork: NeuralNetwork, + TrainStream: TrainStream, + recurrent: { + RNN: RNN, + LSTM: LSTM, + GRU: GRU, + }, + utilities: utilities +}; + +if (typeof window !== 'undefined') { + var brain = window.brain = {}; + var i; -},{}],15:[function(require,module,exports){ + for (i in module.exports) { + brain[i] = module.exports[i].default || module.exports[i]; + } + for (i in module.exports.utilities) { + brain.utilities[i] = module.exports.utilities[i].default || module.exports.utilities[i]; + } + for (i in module.exports.recurrent) { + brain.recurrent[i] = module.exports.recurrent[i].default || module.exports.recurrent[i]; + } +} + +},{"./dist/cross-validate":1,"./dist/likely":2,"./dist/lookup":3,"./dist/neural-network":4,"./dist/recurrent/gru":5,"./dist/recurrent/lstm":6,"./dist/recurrent/rnn":31,"./dist/train-stream":32,"./dist/utilities/max":33,"./dist/utilities/mse":34,"./dist/utilities/ones":35,"./dist/utilities/random":37,"./dist/utilities/random-weight":36,"./dist/utilities/randos":38,"./dist/utilities/range":39,"./dist/utilities/to-array":40,"./dist/utilities/vocab":41,"./dist/utilities/zeros":42}],44:[function(require,module,exports){ 'use strict' exports.toByteArray = toByteArray @@ -1484,9 +3750,9 @@ function fromByteArray (uint8) { return parts.join('') } -},{}],16:[function(require,module,exports){ +},{}],45:[function(require,module,exports){ -},{}],17:[function(require,module,exports){ +},{}],46:[function(require,module,exports){ (function (global){ 'use strict'; @@ -1598,7 +3864,7 @@ exports.allocUnsafeSlow = function allocUnsafeSlow(size) { } }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"buffer":18}],18:[function(require,module,exports){ +},{"buffer":47}],47:[function(require,module,exports){ (function (global){ /*! * The buffer module from node.js, for the browser. @@ -3391,7 +5657,7 @@ function isnan (val) { } }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"base64-js":15,"ieee754":21,"isarray":24}],19:[function(require,module,exports){ +},{"base64-js":44,"ieee754":50,"isarray":53}],48:[function(require,module,exports){ (function (Buffer){ // Copyright Joyent, Inc. and other Node contributors. // @@ -3502,7 +5768,7 @@ function objectToString(o) { } }).call(this,{"isBuffer":require("../../is-buffer/index.js")}) -},{"../../is-buffer/index.js":23}],20:[function(require,module,exports){ +},{"../../is-buffer/index.js":52}],49:[function(require,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -3806,7 +6072,7 @@ function isUndefined(arg) { return arg === void 0; } -},{}],21:[function(require,module,exports){ +},{}],50:[function(require,module,exports){ exports.read = function (buffer, offset, isLE, mLen, nBytes) { var e, m var eLen = nBytes * 8 - mLen - 1 @@ -3892,7 +6158,7 @@ exports.write = function (buffer, value, offset, isLE, mLen, nBytes) { buffer[offset + i - d] |= s * 128 } -},{}],22:[function(require,module,exports){ +},{}],51:[function(require,module,exports){ if (typeof Object.create === 'function') { // implementation from standard node.js 'util' module module.exports = function inherits(ctor, superCtor) { @@ -3917,7 +6183,7 @@ if (typeof Object.create === 'function') { } } -},{}],23:[function(require,module,exports){ +},{}],52:[function(require,module,exports){ /*! * Determine if an object is a Buffer * @@ -3940,14 +6206,14 @@ function isSlowBuffer (obj) { return typeof obj.readFloatLE === 'function' && typeof obj.slice === 'function' && isBuffer(obj.slice(0, 0)) } -},{}],24:[function(require,module,exports){ +},{}],53:[function(require,module,exports){ var toString = {}.toString; module.exports = Array.isArray || function (arr) { return toString.call(arr) == '[object Array]'; }; -},{}],25:[function(require,module,exports){ +},{}],54:[function(require,module,exports){ (function (process){ 'use strict'; @@ -3994,7 +6260,7 @@ function nextTick(fn, arg1, arg2, arg3) { } }).call(this,require('_process')) -},{"_process":26}],26:[function(require,module,exports){ +},{"_process":55}],55:[function(require,module,exports){ // shim for using process in browser var process = module.exports = {}; @@ -4176,10 +6442,10 @@ process.chdir = function (dir) { }; process.umask = function() { return 0; }; -},{}],27:[function(require,module,exports){ +},{}],56:[function(require,module,exports){ module.exports = require("./lib/_stream_duplex.js") -},{"./lib/_stream_duplex.js":28}],28:[function(require,module,exports){ +},{"./lib/_stream_duplex.js":57}],57:[function(require,module,exports){ // a duplex stream is just a stream that is both readable and writable. // Since JS doesn't have multiple prototypal inheritance, this class // prototypally inherits from Readable, and then parasitically from @@ -4255,7 +6521,7 @@ function forEach(xs, f) { f(xs[i], i); } } -},{"./_stream_readable":30,"./_stream_writable":32,"core-util-is":19,"inherits":22,"process-nextick-args":25}],29:[function(require,module,exports){ +},{"./_stream_readable":59,"./_stream_writable":61,"core-util-is":48,"inherits":51,"process-nextick-args":54}],58:[function(require,module,exports){ // a passthrough stream. // basically just the most minimal sort of Transform stream. // Every written chunk gets output as-is. @@ -4282,7 +6548,7 @@ function PassThrough(options) { PassThrough.prototype._transform = function (chunk, encoding, cb) { cb(null, chunk); }; -},{"./_stream_transform":31,"core-util-is":19,"inherits":22}],30:[function(require,module,exports){ +},{"./_stream_transform":60,"core-util-is":48,"inherits":51}],59:[function(require,module,exports){ (function (process){ 'use strict'; @@ -5222,7 +7488,7 @@ function indexOf(xs, x) { return -1; } }).call(this,require('_process')) -},{"./_stream_duplex":28,"./internal/streams/BufferList":33,"_process":26,"buffer":18,"buffer-shims":17,"core-util-is":19,"events":20,"inherits":22,"isarray":24,"process-nextick-args":25,"string_decoder/":39,"util":16}],31:[function(require,module,exports){ +},{"./_stream_duplex":57,"./internal/streams/BufferList":62,"_process":55,"buffer":47,"buffer-shims":46,"core-util-is":48,"events":49,"inherits":51,"isarray":53,"process-nextick-args":54,"string_decoder/":68,"util":45}],60:[function(require,module,exports){ // a transform stream is a readable/writable stream where you do // something with the data. Sometimes it's called a "filter", // but that's not a great name for it, since that implies a thing where @@ -5403,7 +7669,7 @@ function done(stream, er) { return stream.push(null); } -},{"./_stream_duplex":28,"core-util-is":19,"inherits":22}],32:[function(require,module,exports){ +},{"./_stream_duplex":57,"core-util-is":48,"inherits":51}],61:[function(require,module,exports){ (function (process){ // A bit simpler than readable streams. // Implement an async ._write(chunk, encoding, cb), and it'll handle all @@ -5932,7 +8198,7 @@ function CorkedRequest(state) { }; } }).call(this,require('_process')) -},{"./_stream_duplex":28,"_process":26,"buffer":18,"buffer-shims":17,"core-util-is":19,"events":20,"inherits":22,"process-nextick-args":25,"util-deprecate":40}],33:[function(require,module,exports){ +},{"./_stream_duplex":57,"_process":55,"buffer":47,"buffer-shims":46,"core-util-is":48,"events":49,"inherits":51,"process-nextick-args":54,"util-deprecate":69}],62:[function(require,module,exports){ 'use strict'; var Buffer = require('buffer').Buffer; @@ -5997,10 +8263,10 @@ BufferList.prototype.concat = function (n) { } return ret; }; -},{"buffer":18,"buffer-shims":17}],34:[function(require,module,exports){ +},{"buffer":47,"buffer-shims":46}],63:[function(require,module,exports){ module.exports = require("./lib/_stream_passthrough.js") -},{"./lib/_stream_passthrough.js":29}],35:[function(require,module,exports){ +},{"./lib/_stream_passthrough.js":58}],64:[function(require,module,exports){ (function (process){ var Stream = (function (){ try { @@ -6020,13 +8286,13 @@ if (!process.browser && process.env.READABLE_STREAM === 'disable' && Stream) { } }).call(this,require('_process')) -},{"./lib/_stream_duplex.js":28,"./lib/_stream_passthrough.js":29,"./lib/_stream_readable.js":30,"./lib/_stream_transform.js":31,"./lib/_stream_writable.js":32,"_process":26}],36:[function(require,module,exports){ +},{"./lib/_stream_duplex.js":57,"./lib/_stream_passthrough.js":58,"./lib/_stream_readable.js":59,"./lib/_stream_transform.js":60,"./lib/_stream_writable.js":61,"_process":55}],65:[function(require,module,exports){ module.exports = require("./lib/_stream_transform.js") -},{"./lib/_stream_transform.js":31}],37:[function(require,module,exports){ +},{"./lib/_stream_transform.js":60}],66:[function(require,module,exports){ module.exports = require("./lib/_stream_writable.js") -},{"./lib/_stream_writable.js":32}],38:[function(require,module,exports){ +},{"./lib/_stream_writable.js":61}],67:[function(require,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -6155,7 +8421,7 @@ Stream.prototype.pipe = function(dest, options) { return dest; }; -},{"events":20,"inherits":22,"readable-stream/duplex.js":27,"readable-stream/passthrough.js":34,"readable-stream/readable.js":35,"readable-stream/transform.js":36,"readable-stream/writable.js":37}],39:[function(require,module,exports){ +},{"events":49,"inherits":51,"readable-stream/duplex.js":56,"readable-stream/passthrough.js":63,"readable-stream/readable.js":64,"readable-stream/transform.js":65,"readable-stream/writable.js":66}],68:[function(require,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -6378,7 +8644,7 @@ function base64DetectIncompleteChar(buffer) { this.charLength = this.charReceived ? 3 : 0; } -},{"buffer":18}],40:[function(require,module,exports){ +},{"buffer":47}],69:[function(require,module,exports){ (function (global){ /** @@ -6449,4 +8715,4 @@ function config (name) { } }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{}]},{},[1,2,3,4,5,6,7,8,9,10,11,12,13,14]); +},{}]},{},[43]); diff --git a/browser.min.js b/browser.min.js index d012626aa..33a41658c 100644 --- a/browser.min.js +++ b/browser.min.js @@ -1,25 +1,321 @@ -/* -Copyright (c) 2010-2016 Heather Arthur - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/!function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};b[g][0].call(k.exports,function(a){var c=b[g][1][a];return e(c?c:a)},k,k.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g0;b--){var c=Math.floor(Math.random()*(b+1)),d=a[b];a[b]=a[c],a[c]=d}return a}function f(a,b,c,f,g){g=g||4;var h=b.length/g;b.constructor===Array?e(b):!function(){var a={};e(Object.keys(b)).forEach(function(c){a[c]=b[c]}),b=a}();for(var i={error:0,trainTime:0,testTime:0,iterations:0,trainError:0},j={truePos:0,trueNeg:0,falsePos:0,falseNeg:0,total:0},k=[],l=[],m=void 0,n=void 0,o=0;g>o;o++){var p=b.slice(0),q=p.splice(o*h,h),r=p,s=d(a,c,f,r,q);for(m in i)m in i&&(n=i[m],i[m]=n+s[m]);for(m in j)m in j&&(n=j[m],j[m]=n+s[m]);k.concat(l.misclasses),l.push(s)}for(m in i)m in i&&(n=i[m],i[m]=n/g);return j.precision=j.truePos/(j.truePos+j.falsePos),j.recall=j.truePos/(j.truePos+j.falseNeg),j.accuracy=(j.trueNeg+j.truePos)/j.total,j.testSize=h,j.trainSize=b.length-h,{avgs:i,stats:j,sets:l,misclasses:k}}Object.defineProperty(c,"__esModule",{value:!0}),c.testPartition=d,c.shuffleArray=e,c["default"]=f},{}],3:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{"default":a}}Object.defineProperty(c,"__esModule",{value:!0});var e=a("./cross-validate"),f=d(e),g=a("./likely"),h=d(g),i=a("./lookup"),j=d(i),k=a("./neural-network"),l=d(k),m=a("./train-stream"),n=d(m);c["default"]={crossValidate:f["default"],likely:h["default"],lookup:j["default"],NeuralNetwork:l["default"],TrainStream:n["default"]}},{"./cross-validate":2,"./likely":4,"./lookup":5,"./neural-network":6,"./train-stream":7}],4:[function(a,b,c){"use strict";function d(a,b){var c=b.run(a),d=null,e=-1;for(var f in c)if(f in c){var g=c[f];g>e&&(d=f,e=g)}return d}Object.defineProperty(c,"__esModule",{value:!0}),c["default"]=d},{}],5:[function(a,b,c){"use strict";function d(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(c,"__esModule",{value:!0});var e=function(){function a(a,b){for(var c=0;c0;)b[a[d]]=c++;return b}}]),a}();c["default"]=f},{}],6:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{"default":a}}function e(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(c,"__esModule",{value:!0});var f=function(){function a(a,b){for(var c=0;c0){this.biases[c]=p["default"](d),b||(this.weights[c]=new Array(d)),this.changes[c]=new Array(d);for(var e=0;d>e;e++){var f=this.sizes[c-1];b||(this.weights[c][e]=p["default"](f)),this.changes[c][e]=v["default"](f)}}}}},{key:"run",value:function(a){this.inputLookup&&(a=h["default"].toArray(this.inputLookup,a));var b=this.runInput(a);return this.outputLookup&&(b=h["default"].toHash(this.outputLookup,b)),b}},{key:"runInput",value:function(a){this.outputs[0]=a;for(var b=null,c=1;c<=this.outputLayer;c++){for(var d=0;do&&n>d;o++){for(var p=0,q=0;q=0;b--)for(var c=0;cb.binaryThresh?1:0,p=m[0]):(o=k.indexOf(l["default"](k)),p=m.indexOf(l["default"](m))),o!=p){var q=a[j];Object.assign(q,{actual:o,expected:p}),h.push(q)}c&&(0==o&&0==p?g++:1==o&&1==p?f++:0==o&&1==p?e++:1==o&&0==p&&d++);var r=k.map(function(a,b){return m[b]-a});i+=n["default"](r)},k=0;k0){a[b][e].bias=this.biases[b][d],a[b][e].weights={};for(var f in a[b-1]){var g=f;1==b&&this.inputLookup&&(g=this.inputLookup[f]),a[b][e].weights[f]=this.weights[b][d][g]}}}}return{layers:a,outputLookup:!!this.outputLookup,inputLookup:!!this.inputLookup}}},{key:"fromJSON",value:function(a){var b=a.layers.length;this.outputLayer=b-1,this.sizes=new Array(b),this.weights=new Array(b),this.biases=new Array(b),this.outputs=new Array(b);for(var c=0;c<=this.outputLayer;c++){var d=a.layers[c];0!=c||d[0]&&!a.inputLookup?c!=this.outputLayer||d[0]&&!a.outputLookup||(this.outputLookup=h["default"].lookupFromHash(d)):this.inputLookup=h["default"].lookupFromHash(d);var e=Object.keys(d);this.sizes[c]=e.length,this.weights[c]=[],this.biases[c]=[],this.outputs[c]=[];for(var f in e){var g=e[f];this.biases[c][f]=d[g].bias,this.weights[c][f]=t["default"](d[g].weights)}}return this}},{key:"toFunction",value:function(){var a=this.toJSON(),b=JSON.stringify(a);return new Function("input","\n var net = "+b+";\n for (var i = 1; i < net.layers.length; i++) {\n var layer = net.layers[i];\n var output = {};\n \n for (var id in layer) {\n var node = layer[id];\n var sum = node.bias;\n \n for (var iid in node.weights) {\n sum += node.weights[iid] * input[iid];\n }\n output[id] = (1 / (1 + Math.exp(-sum)));\n }\n input = output;\n }\n return output;\n ")}},{key:"createTrainStream",value:function(a){return a=a||{},a.neuralNetwork=this,this.trainStream=new j["default"](a),this.trainStream}}]),a}();c["default"]=w},{"./lookup":5,"./train-stream":7,"./utilities/max":8,"./utilities/mse":9,"./utilities/randos":11,"./utilities/range":12,"./utilities/to-array":13,"./utilities/zeros":14}],7:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{"default":a}}function e(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}function f(a,b){if(!a)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!b||"object"!=typeof b&&"function"!=typeof b?a:b}function g(a,b){if("function"!=typeof b&&null!==b)throw new TypeError("Super expression must either be null or a function, not "+typeof b);a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,enumerable:!1,writable:!0,configurable:!0}}),b&&(Object.setPrototypeOf?Object.setPrototypeOf(a,b):a.__proto__=b)}function h(a){for(var b=[],c=0,d=a.length;d>c;c++)-1===b.indexOf(a[c])&&""!==a[c]&&b.push(a[c]);return b}Object.defineProperty(c,"__esModule",{value:!0});var i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol?"symbol":typeof a},j=function(){function a(a,b){for(var c=0;cthis.errorThresh){if("function"==typeof this.floodCallback)return this.floodCallback()}else if("function"==typeof this.doneTrainingCallback)return this.doneTrainingCallback({error:c,iterations:this.i})}}]),b}(k.Writable);c["default"]=n},{"./lookup":5,stream:38}],8:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{"default":a}}function e(a){return Math.max.apply(Math,g["default"](a))}Object.defineProperty(c,"__esModule",{value:!0}),c["default"]=e;var f=a("./to-array"),g=d(f)},{"./to-array":13}],9:[function(a,b,c){"use strict";function d(a){for(var b=0,c=0;cc;c++)b[c]=g["default"]();return b}Object.defineProperty(c,"__esModule",{value:!0}),c["default"]=e;var f=a("./random-weight"),g=d(f)},{"./random-weight":10}],12:[function(a,b,c){"use strict";function d(a,b){for(var c=[];b>a;a++)c.push(a);return c}Object.defineProperty(c,"__esModule",{value:!0}),c["default"]=d},{}],13:[function(a,b,c){"use strict";function d(a){return a=a||[],a.constructor===Array?a:Object.keys(a).map(function(b){return a[b]})}Object.defineProperty(c,"__esModule",{value:!0}),c["default"]=d},{}],14:[function(a,b,c){"use strict";function d(a){for(var b=new Array(a),c=0;a>c;c++)b[c]=0;return b}Object.defineProperty(c,"__esModule",{value:!0}),c["default"]=d},{}],15:[function(a,b,c){"use strict";function d(){for(var a="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",b=0,c=a.length;c>b;++b)i[b]=a[b],j[a.charCodeAt(b)]=b;j["-".charCodeAt(0)]=62,j["_".charCodeAt(0)]=63}function e(a){var b,c,d,e,f,g,h=a.length;if(h%4>0)throw new Error("Invalid string. Length must be a multiple of 4");f="="===a[h-2]?2:"="===a[h-1]?1:0,g=new k(3*h/4-f),d=f>0?h-4:h;var i=0;for(b=0,c=0;d>b;b+=4,c+=3)e=j[a.charCodeAt(b)]<<18|j[a.charCodeAt(b+1)]<<12|j[a.charCodeAt(b+2)]<<6|j[a.charCodeAt(b+3)],g[i++]=e>>16&255,g[i++]=e>>8&255,g[i++]=255&e;return 2===f?(e=j[a.charCodeAt(b)]<<2|j[a.charCodeAt(b+1)]>>4,g[i++]=255&e):1===f&&(e=j[a.charCodeAt(b)]<<10|j[a.charCodeAt(b+1)]<<4|j[a.charCodeAt(b+2)]>>2,g[i++]=e>>8&255,g[i++]=255&e),g}function f(a){return i[a>>18&63]+i[a>>12&63]+i[a>>6&63]+i[63&a]}function g(a,b,c){for(var d,e=[],g=b;c>g;g+=3)d=(a[g]<<16)+(a[g+1]<<8)+a[g+2],e.push(f(d));return e.join("")}function h(a){for(var b,c=a.length,d=c%3,e="",f=[],h=16383,j=0,k=c-d;k>j;j+=h)f.push(g(a,j,j+h>k?k:j+h));return 1===d?(b=a[c-1],e+=i[b>>2],e+=i[b<<4&63],e+="=="):2===d&&(b=(a[c-2]<<8)+a[c-1],e+=i[b>>10],e+=i[b>>4&63],e+=i[b<<2&63],e+="="),f.push(e),f.join("")}c.toByteArray=e,c.fromByteArray=h;var i=[],j=[],k="undefined"!=typeof Uint8Array?Uint8Array:Array;d()},{}],16:[function(a,b,c){},{}],17:[function(a,b,c){(function(b){"use strict";var d=a("buffer"),e=d.Buffer,f=d.SlowBuffer,g=d.kMaxLength||2147483647;c.alloc=function(a,b,c){if("function"==typeof e.alloc)return e.alloc(a,b,c);if("number"==typeof c)throw new TypeError("encoding must not be number");if("number"!=typeof a)throw new TypeError("size must be a number");if(a>g)throw new RangeError("size is too large");var d=c,f=b;void 0===f&&(d=void 0,f=0);var h=new e(a);if("string"==typeof f)for(var i=new e(f,d),j=i.length,k=-1;++kg)throw new RangeError("size is too large");return new e(a)},c.from=function(a,c,d){if("function"==typeof e.from&&(!b.Uint8Array||Uint8Array.from!==e.from))return e.from(a,c,d);if("number"==typeof a)throw new TypeError('"value" argument must not be a number');if("string"==typeof a)return new e(a,c);if("undefined"!=typeof ArrayBuffer&&a instanceof ArrayBuffer){var f=c;if(1===arguments.length)return new e(a);"undefined"==typeof f&&(f=0);var g=d;if("undefined"==typeof g&&(g=a.byteLength-f),f>=a.byteLength)throw new RangeError("'offset' is out of bounds");if(g>a.byteLength-f)throw new RangeError("'length' is out of bounds");return new e(a.slice(f,f+g))}if(e.isBuffer(a)){var h=new e(a.length);return a.copy(h,0,0,a.length),h}if(a){if(Array.isArray(a)||"undefined"!=typeof ArrayBuffer&&a.buffer instanceof ArrayBuffer||"length"in a)return new e(a);if("Buffer"===a.type&&Array.isArray(a.data))return new e(a.data)}throw new TypeError("First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.")},c.allocUnsafeSlow=function(a){if("function"==typeof e.allocUnsafeSlow)return e.allocUnsafeSlow(a);if("number"!=typeof a)throw new TypeError("size must be a number");if(a>=g)throw new RangeError("size is too large");return new f(a)}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{buffer:18}],18:[function(a,b,c){(function(b){"use strict";function d(){try{var a=new Uint8Array(1);return a.__proto__={__proto__:Uint8Array.prototype,foo:function(){return 42}},42===a.foo()&&"function"==typeof a.subarray&&0===a.subarray(1,1).byteLength}catch(b){return!1}}function e(){return g.TYPED_ARRAY_SUPPORT?2147483647:1073741823}function f(a,b){if(e()a)throw new RangeError('"size" argument must not be negative')}function j(a,b,c,d){return i(b),0>=b?f(a,b):void 0!==c?"string"==typeof d?f(a,b).fill(c,d):f(a,b).fill(c):f(a,b)}function k(a,b){if(i(b),a=f(a,0>b?0:0|p(b)),!g.TYPED_ARRAY_SUPPORT)for(var c=0;b>c;++c)a[c]=0;return a}function l(a,b,c){if(("string"!=typeof c||""===c)&&(c="utf8"),!g.isEncoding(c))throw new TypeError('"encoding" must be a valid string encoding');var d=0|r(b,c);a=f(a,d);var e=a.write(b,c);return e!==d&&(a=a.slice(0,e)),a}function m(a,b){var c=b.length<0?0:0|p(b.length);a=f(a,c);for(var d=0;c>d;d+=1)a[d]=255&b[d];return a}function n(a,b,c,d){if(b.byteLength,0>c||b.byteLength=e())throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+e().toString(16)+" bytes");return 0|a}function q(a){return+a!=a&&(a=0),g.alloc(+a)}function r(a,b){if(g.isBuffer(a))return a.length;if("undefined"!=typeof ArrayBuffer&&"function"==typeof ArrayBuffer.isView&&(ArrayBuffer.isView(a)||a instanceof ArrayBuffer))return a.byteLength;"string"!=typeof a&&(a=""+a);var c=a.length;if(0===c)return 0;for(var d=!1;;)switch(b){case"ascii":case"latin1":case"binary":return c;case"utf8":case"utf-8":case void 0:return T(a).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*c;case"hex":return c>>>1;case"base64":return W(a).length;default:if(d)return T(a).length;b=(""+b).toLowerCase(),d=!0}}function s(a,b,c){var d=!1;if((void 0===b||0>b)&&(b=0),b>this.length)return"";if((void 0===c||c>this.length)&&(c=this.length),0>=c)return"";if(c>>>=0,b>>>=0,b>=c)return"";for(a||(a="utf8");;)switch(a){case"hex":return H(this,b,c);case"utf8":case"utf-8":return D(this,b,c);case"ascii":return F(this,b,c);case"latin1":case"binary":return G(this,b,c);case"base64":return C(this,b,c);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return I(this,b,c);default:if(d)throw new TypeError("Unknown encoding: "+a);a=(a+"").toLowerCase(),d=!0}}function t(a,b,c){var d=a[b];a[b]=a[c],a[c]=d}function u(a,b,c,d,e){if(0===a.length)return-1;if("string"==typeof c?(d=c,c=0):c>2147483647?c=2147483647:-2147483648>c&&(c=-2147483648),c=+c,isNaN(c)&&(c=e?0:a.length-1),0>c&&(c=a.length+c),c>=a.length){if(e)return-1;c=a.length-1}else if(0>c){if(!e)return-1;c=0}if("string"==typeof b&&(b=g.from(b,d)),g.isBuffer(b))return 0===b.length?-1:v(a,b,c,d,e);if("number"==typeof b)return b=255&b,g.TYPED_ARRAY_SUPPORT&&"function"==typeof Uint8Array.prototype.indexOf?e?Uint8Array.prototype.indexOf.call(a,b,c):Uint8Array.prototype.lastIndexOf.call(a,b,c):v(a,[b],c,d,e);throw new TypeError("val must be string, number or Buffer")}function v(a,b,c,d,e){function f(a,b){return 1===g?a[b]:a.readUInt16BE(b*g)}var g=1,h=a.length,i=b.length;if(void 0!==d&&(d=String(d).toLowerCase(),"ucs2"===d||"ucs-2"===d||"utf16le"===d||"utf-16le"===d)){if(a.length<2||b.length<2)return-1;g=2,h/=2,i/=2,c/=2}var j;if(e){var k=-1;for(j=c;h>j;j++)if(f(a,j)===f(b,-1===k?0:j-k)){if(-1===k&&(k=j),j-k+1===i)return k*g}else-1!==k&&(j-=j-k),k=-1}else for(c+i>h&&(c=h-i),j=c;j>=0;j--){for(var l=!0,m=0;i>m;m++)if(f(a,j+m)!==f(b,m)){l=!1;break}if(l)return j}return-1}function w(a,b,c,d){c=Number(c)||0;var e=a.length-c;d?(d=Number(d),d>e&&(d=e)):d=e;var f=b.length;if(f%2!==0)throw new TypeError("Invalid hex string");d>f/2&&(d=f/2);for(var g=0;d>g;++g){var h=parseInt(b.substr(2*g,2),16);if(isNaN(h))return g;a[c+g]=h}return g}function x(a,b,c,d){return X(T(b,a.length-c),a,c,d)}function y(a,b,c,d){return X(U(b),a,c,d)}function z(a,b,c,d){return y(a,b,c,d)}function A(a,b,c,d){return X(W(b),a,c,d)}function B(a,b,c,d){return X(V(b,a.length-c),a,c,d)}function C(a,b,c){return 0===b&&c===a.length?Z.fromByteArray(a):Z.fromByteArray(a.slice(b,c))}function D(a,b,c){c=Math.min(a.length,c);for(var d=[],e=b;c>e;){var f=a[e],g=null,h=f>239?4:f>223?3:f>191?2:1;if(c>=e+h){var i,j,k,l;switch(h){case 1:128>f&&(g=f);break;case 2:i=a[e+1],128===(192&i)&&(l=(31&f)<<6|63&i,l>127&&(g=l));break;case 3:i=a[e+1],j=a[e+2],128===(192&i)&&128===(192&j)&&(l=(15&f)<<12|(63&i)<<6|63&j,l>2047&&(55296>l||l>57343)&&(g=l));break;case 4:i=a[e+1],j=a[e+2],k=a[e+3],128===(192&i)&&128===(192&j)&&128===(192&k)&&(l=(15&f)<<18|(63&i)<<12|(63&j)<<6|63&k,l>65535&&1114112>l&&(g=l))}}null===g?(g=65533,h=1):g>65535&&(g-=65536,d.push(g>>>10&1023|55296),g=56320|1023&g),d.push(g),e+=h}return E(d)}function E(a){var b=a.length;if(aa>=b)return String.fromCharCode.apply(String,a);for(var c="",d=0;b>d;)c+=String.fromCharCode.apply(String,a.slice(d,d+=aa));return c}function F(a,b,c){var d="";c=Math.min(a.length,c);for(var e=b;c>e;++e)d+=String.fromCharCode(127&a[e]);return d}function G(a,b,c){var d="";c=Math.min(a.length,c);for(var e=b;c>e;++e)d+=String.fromCharCode(a[e]);return d}function H(a,b,c){var d=a.length;(!b||0>b)&&(b=0),(!c||0>c||c>d)&&(c=d);for(var e="",f=b;c>f;++f)e+=S(a[f]);return e}function I(a,b,c){for(var d=a.slice(b,c),e="",f=0;fa)throw new RangeError("offset is not uint");if(a+b>c)throw new RangeError("Trying to access beyond buffer length")}function K(a,b,c,d,e,f){if(!g.isBuffer(a))throw new TypeError('"buffer" argument must be a Buffer instance');if(b>e||f>b)throw new RangeError('"value" argument is out of bounds');if(c+d>a.length)throw new RangeError("Index out of range")}function L(a,b,c,d){0>b&&(b=65535+b+1);for(var e=0,f=Math.min(a.length-c,2);f>e;++e)a[c+e]=(b&255<<8*(d?e:1-e))>>>8*(d?e:1-e)}function M(a,b,c,d){0>b&&(b=4294967295+b+1);for(var e=0,f=Math.min(a.length-c,4);f>e;++e)a[c+e]=b>>>8*(d?e:3-e)&255}function N(a,b,c,d,e,f){if(c+d>a.length)throw new RangeError("Index out of range");if(0>c)throw new RangeError("Index out of range")}function O(a,b,c,d,e){return e||N(a,b,c,4,3.4028234663852886e38,-3.4028234663852886e38),$.write(a,b,c,d,23,4),c+4}function P(a,b,c,d,e){return e||N(a,b,c,8,1.7976931348623157e308,-1.7976931348623157e308),$.write(a,b,c,d,52,8),c+8}function Q(a){if(a=R(a).replace(ba,""),a.length<2)return"";for(;a.length%4!==0;)a+="=";return a}function R(a){return a.trim?a.trim():a.replace(/^\s+|\s+$/g,"")}function S(a){return 16>a?"0"+a.toString(16):a.toString(16)}function T(a,b){b=b||1/0;for(var c,d=a.length,e=null,f=[],g=0;d>g;++g){if(c=a.charCodeAt(g),c>55295&&57344>c){if(!e){if(c>56319){(b-=3)>-1&&f.push(239,191,189);continue}if(g+1===d){(b-=3)>-1&&f.push(239,191,189);continue}e=c;continue}if(56320>c){(b-=3)>-1&&f.push(239,191,189),e=c;continue}c=(e-55296<<10|c-56320)+65536}else e&&(b-=3)>-1&&f.push(239,191,189);if(e=null,128>c){if((b-=1)<0)break;f.push(c)}else if(2048>c){if((b-=2)<0)break;f.push(c>>6|192,63&c|128)}else if(65536>c){if((b-=3)<0)break;f.push(c>>12|224,c>>6&63|128,63&c|128)}else{if(!(1114112>c))throw new Error("Invalid code point");if((b-=4)<0)break;f.push(c>>18|240,c>>12&63|128,c>>6&63|128,63&c|128)}}return f}function U(a){for(var b=[],c=0;c>8,e=c%256,f.push(e),f.push(d);return f}function W(a){return Z.toByteArray(Q(a))}function X(a,b,c,d){for(var e=0;d>e&&!(e+c>=b.length||e>=a.length);++e)b[e+c]=a[e];return e}function Y(a){return a!==a}var Z=a("base64-js"),$=a("ieee754"),_=a("isarray");c.Buffer=g,c.SlowBuffer=q,c.INSPECT_MAX_BYTES=50,g.TYPED_ARRAY_SUPPORT=void 0!==b.TYPED_ARRAY_SUPPORT?b.TYPED_ARRAY_SUPPORT:d(),c.kMaxLength=e(),g.poolSize=8192,g._augment=function(a){return a.__proto__=g.prototype,a},g.from=function(a,b,c){return h(null,a,b,c)},g.TYPED_ARRAY_SUPPORT&&(g.prototype.__proto__=Uint8Array.prototype,g.__proto__=Uint8Array,"undefined"!=typeof Symbol&&Symbol.species&&g[Symbol.species]===g&&Object.defineProperty(g,Symbol.species,{value:null,configurable:!0})),g.alloc=function(a,b,c){return j(null,a,b,c)},g.allocUnsafe=function(a){return k(null,a)},g.allocUnsafeSlow=function(a){return k(null,a)},g.isBuffer=function(a){return!(null==a||!a._isBuffer)},g.compare=function(a,b){if(!g.isBuffer(a)||!g.isBuffer(b))throw new TypeError("Arguments must be Buffers");if(a===b)return 0;for(var c=a.length,d=b.length,e=0,f=Math.min(c,d);f>e;++e)if(a[e]!==b[e]){c=a[e],d=b[e];break}return d>c?-1:c>d?1:0},g.isEncoding=function(a){switch(String(a).toLowerCase()){case"hex":case"utf8":case"utf-8":case"ascii":case"latin1":case"binary":case"base64":case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return!0;default:return!1}},g.concat=function(a,b){if(!_(a))throw new TypeError('"list" argument must be an Array of Buffers');if(0===a.length)return g.alloc(0);var c;if(void 0===b)for(b=0,c=0;cb;b+=2)t(this,b,b+1);return this},g.prototype.swap32=function(){var a=this.length;if(a%4!==0)throw new RangeError("Buffer size must be a multiple of 32-bits");for(var b=0;a>b;b+=4)t(this,b,b+3),t(this,b+1,b+2);return this},g.prototype.swap64=function(){var a=this.length;if(a%8!==0)throw new RangeError("Buffer size must be a multiple of 64-bits");for(var b=0;a>b;b+=8)t(this,b,b+7),t(this,b+1,b+6),t(this,b+2,b+5),t(this,b+3,b+4);return this},g.prototype.toString=function(){var a=0|this.length;return 0===a?"":0===arguments.length?D(this,0,a):s.apply(this,arguments)},g.prototype.equals=function(a){if(!g.isBuffer(a))throw new TypeError("Argument must be a Buffer");return this===a?!0:0===g.compare(this,a)},g.prototype.inspect=function(){var a="",b=c.INSPECT_MAX_BYTES;return this.length>0&&(a=this.toString("hex",0,b).match(/.{2}/g).join(" "),this.length>b&&(a+=" ... ")),""},g.prototype.compare=function(a,b,c,d,e){if(!g.isBuffer(a))throw new TypeError("Argument must be a Buffer");if(void 0===b&&(b=0),void 0===c&&(c=a?a.length:0),void 0===d&&(d=0),void 0===e&&(e=this.length),0>b||c>a.length||0>d||e>this.length)throw new RangeError("out of range index");if(d>=e&&b>=c)return 0;if(d>=e)return-1;if(b>=c)return 1;if(b>>>=0,c>>>=0,d>>>=0,e>>>=0,this===a)return 0;for(var f=e-d,h=c-b,i=Math.min(f,h),j=this.slice(d,e),k=a.slice(b,c),l=0;i>l;++l)if(j[l]!==k[l]){f=j[l],h=k[l];break}return h>f?-1:f>h?1:0},g.prototype.includes=function(a,b,c){return-1!==this.indexOf(a,b,c)},g.prototype.indexOf=function(a,b,c){return u(this,a,b,c,!0)},g.prototype.lastIndexOf=function(a,b,c){return u(this,a,b,c,!1)},g.prototype.write=function(a,b,c,d){ -if(void 0===b)d="utf8",c=this.length,b=0;else if(void 0===c&&"string"==typeof b)d=b,c=this.length,b=0;else{if(!isFinite(b))throw new Error("Buffer.write(string, encoding, offset[, length]) is no longer supported");b=0|b,isFinite(c)?(c=0|c,void 0===d&&(d="utf8")):(d=c,c=void 0)}var e=this.length-b;if((void 0===c||c>e)&&(c=e),a.length>0&&(0>c||0>b)||b>this.length)throw new RangeError("Attempt to write outside buffer bounds");d||(d="utf8");for(var f=!1;;)switch(d){case"hex":return w(this,a,b,c);case"utf8":case"utf-8":return x(this,a,b,c);case"ascii":return y(this,a,b,c);case"latin1":case"binary":return z(this,a,b,c);case"base64":return A(this,a,b,c);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return B(this,a,b,c);default:if(f)throw new TypeError("Unknown encoding: "+d);d=(""+d).toLowerCase(),f=!0}},g.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};var aa=4096;g.prototype.slice=function(a,b){var c=this.length;a=~~a,b=void 0===b?c:~~b,0>a?(a+=c,0>a&&(a=0)):a>c&&(a=c),0>b?(b+=c,0>b&&(b=0)):b>c&&(b=c),a>b&&(b=a);var d;if(g.TYPED_ARRAY_SUPPORT)d=this.subarray(a,b),d.__proto__=g.prototype;else{var e=b-a;d=new g(e,void 0);for(var f=0;e>f;++f)d[f]=this[f+a]}return d},g.prototype.readUIntLE=function(a,b,c){a=0|a,b=0|b,c||J(a,b,this.length);for(var d=this[a],e=1,f=0;++f0&&(e*=256);)d+=this[a+--b]*e;return d},g.prototype.readUInt8=function(a,b){return b||J(a,1,this.length),this[a]},g.prototype.readUInt16LE=function(a,b){return b||J(a,2,this.length),this[a]|this[a+1]<<8},g.prototype.readUInt16BE=function(a,b){return b||J(a,2,this.length),this[a]<<8|this[a+1]},g.prototype.readUInt32LE=function(a,b){return b||J(a,4,this.length),(this[a]|this[a+1]<<8|this[a+2]<<16)+16777216*this[a+3]},g.prototype.readUInt32BE=function(a,b){return b||J(a,4,this.length),16777216*this[a]+(this[a+1]<<16|this[a+2]<<8|this[a+3])},g.prototype.readIntLE=function(a,b,c){a=0|a,b=0|b,c||J(a,b,this.length);for(var d=this[a],e=1,f=0;++f=e&&(d-=Math.pow(2,8*b)),d},g.prototype.readIntBE=function(a,b,c){a=0|a,b=0|b,c||J(a,b,this.length);for(var d=b,e=1,f=this[a+--d];d>0&&(e*=256);)f+=this[a+--d]*e;return e*=128,f>=e&&(f-=Math.pow(2,8*b)),f},g.prototype.readInt8=function(a,b){return b||J(a,1,this.length),128&this[a]?-1*(255-this[a]+1):this[a]},g.prototype.readInt16LE=function(a,b){b||J(a,2,this.length);var c=this[a]|this[a+1]<<8;return 32768&c?4294901760|c:c},g.prototype.readInt16BE=function(a,b){b||J(a,2,this.length);var c=this[a+1]|this[a]<<8;return 32768&c?4294901760|c:c},g.prototype.readInt32LE=function(a,b){return b||J(a,4,this.length),this[a]|this[a+1]<<8|this[a+2]<<16|this[a+3]<<24},g.prototype.readInt32BE=function(a,b){return b||J(a,4,this.length),this[a]<<24|this[a+1]<<16|this[a+2]<<8|this[a+3]},g.prototype.readFloatLE=function(a,b){return b||J(a,4,this.length),$.read(this,a,!0,23,4)},g.prototype.readFloatBE=function(a,b){return b||J(a,4,this.length),$.read(this,a,!1,23,4)},g.prototype.readDoubleLE=function(a,b){return b||J(a,8,this.length),$.read(this,a,!0,52,8)},g.prototype.readDoubleBE=function(a,b){return b||J(a,8,this.length),$.read(this,a,!1,52,8)},g.prototype.writeUIntLE=function(a,b,c,d){if(a=+a,b=0|b,c=0|c,!d){var e=Math.pow(2,8*c)-1;K(this,a,b,c,e,0)}var f=1,g=0;for(this[b]=255&a;++g=0&&(g*=256);)this[b+f]=a/g&255;return b+c},g.prototype.writeUInt8=function(a,b,c){return a=+a,b=0|b,c||K(this,a,b,1,255,0),g.TYPED_ARRAY_SUPPORT||(a=Math.floor(a)),this[b]=255&a,b+1},g.prototype.writeUInt16LE=function(a,b,c){return a=+a,b=0|b,c||K(this,a,b,2,65535,0),g.TYPED_ARRAY_SUPPORT?(this[b]=255&a,this[b+1]=a>>>8):L(this,a,b,!0),b+2},g.prototype.writeUInt16BE=function(a,b,c){return a=+a,b=0|b,c||K(this,a,b,2,65535,0),g.TYPED_ARRAY_SUPPORT?(this[b]=a>>>8,this[b+1]=255&a):L(this,a,b,!1),b+2},g.prototype.writeUInt32LE=function(a,b,c){return a=+a,b=0|b,c||K(this,a,b,4,4294967295,0),g.TYPED_ARRAY_SUPPORT?(this[b+3]=a>>>24,this[b+2]=a>>>16,this[b+1]=a>>>8,this[b]=255&a):M(this,a,b,!0),b+4},g.prototype.writeUInt32BE=function(a,b,c){return a=+a,b=0|b,c||K(this,a,b,4,4294967295,0),g.TYPED_ARRAY_SUPPORT?(this[b]=a>>>24,this[b+1]=a>>>16,this[b+2]=a>>>8,this[b+3]=255&a):M(this,a,b,!1),b+4},g.prototype.writeIntLE=function(a,b,c,d){if(a=+a,b=0|b,!d){var e=Math.pow(2,8*c-1);K(this,a,b,c,e-1,-e)}var f=0,g=1,h=0;for(this[b]=255&a;++fa&&0===h&&0!==this[b+f-1]&&(h=1),this[b+f]=(a/g>>0)-h&255;return b+c},g.prototype.writeIntBE=function(a,b,c,d){if(a=+a,b=0|b,!d){var e=Math.pow(2,8*c-1);K(this,a,b,c,e-1,-e)}var f=c-1,g=1,h=0;for(this[b+f]=255&a;--f>=0&&(g*=256);)0>a&&0===h&&0!==this[b+f+1]&&(h=1),this[b+f]=(a/g>>0)-h&255;return b+c},g.prototype.writeInt8=function(a,b,c){return a=+a,b=0|b,c||K(this,a,b,1,127,-128),g.TYPED_ARRAY_SUPPORT||(a=Math.floor(a)),0>a&&(a=255+a+1),this[b]=255&a,b+1},g.prototype.writeInt16LE=function(a,b,c){return a=+a,b=0|b,c||K(this,a,b,2,32767,-32768),g.TYPED_ARRAY_SUPPORT?(this[b]=255&a,this[b+1]=a>>>8):L(this,a,b,!0),b+2},g.prototype.writeInt16BE=function(a,b,c){return a=+a,b=0|b,c||K(this,a,b,2,32767,-32768),g.TYPED_ARRAY_SUPPORT?(this[b]=a>>>8,this[b+1]=255&a):L(this,a,b,!1),b+2},g.prototype.writeInt32LE=function(a,b,c){return a=+a,b=0|b,c||K(this,a,b,4,2147483647,-2147483648),g.TYPED_ARRAY_SUPPORT?(this[b]=255&a,this[b+1]=a>>>8,this[b+2]=a>>>16,this[b+3]=a>>>24):M(this,a,b,!0),b+4},g.prototype.writeInt32BE=function(a,b,c){return a=+a,b=0|b,c||K(this,a,b,4,2147483647,-2147483648),0>a&&(a=4294967295+a+1),g.TYPED_ARRAY_SUPPORT?(this[b]=a>>>24,this[b+1]=a>>>16,this[b+2]=a>>>8,this[b+3]=255&a):M(this,a,b,!1),b+4},g.prototype.writeFloatLE=function(a,b,c){return O(this,a,b,!0,c)},g.prototype.writeFloatBE=function(a,b,c){return O(this,a,b,!1,c)},g.prototype.writeDoubleLE=function(a,b,c){return P(this,a,b,!0,c)},g.prototype.writeDoubleBE=function(a,b,c){return P(this,a,b,!1,c)},g.prototype.copy=function(a,b,c,d){if(c||(c=0),d||0===d||(d=this.length),b>=a.length&&(b=a.length),b||(b=0),d>0&&c>d&&(d=c),d===c)return 0;if(0===a.length||0===this.length)return 0;if(0>b)throw new RangeError("targetStart out of bounds");if(0>c||c>=this.length)throw new RangeError("sourceStart out of bounds");if(0>d)throw new RangeError("sourceEnd out of bounds");d>this.length&&(d=this.length),a.length-bc&&d>b)for(e=f-1;e>=0;--e)a[e+b]=this[e+c];else if(1e3>f||!g.TYPED_ARRAY_SUPPORT)for(e=0;f>e;++e)a[e+b]=this[e+c];else Uint8Array.prototype.set.call(a,this.subarray(c,c+f),b);return f},g.prototype.fill=function(a,b,c,d){if("string"==typeof a){if("string"==typeof b?(d=b,b=0,c=this.length):"string"==typeof c&&(d=c,c=this.length),1===a.length){var e=a.charCodeAt(0);256>e&&(a=e)}if(void 0!==d&&"string"!=typeof d)throw new TypeError("encoding must be a string");if("string"==typeof d&&!g.isEncoding(d))throw new TypeError("Unknown encoding: "+d)}else"number"==typeof a&&(a=255&a);if(0>b||this.length=c)return this;b>>>=0,c=void 0===c?this.length:c>>>0,a||(a=0);var f;if("number"==typeof a)for(f=b;c>f;++f)this[f]=a;else{var h=g.isBuffer(a)?a:T(new g(a,d).toString()),i=h.length;for(f=0;c-b>f;++f)this[f+b]=h[f%i]}return this};var ba=/[^+\/0-9A-Za-z-_]/g}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"base64-js":15,ieee754:21,isarray:24}],19:[function(a,b,c){(function(a){function b(a){return Array.isArray?Array.isArray(a):"[object Array]"===q(a)}function d(a){return"boolean"==typeof a}function e(a){return null===a}function f(a){return null==a}function g(a){return"number"==typeof a}function h(a){return"string"==typeof a}function i(a){return"symbol"==typeof a}function j(a){return void 0===a}function k(a){return"[object RegExp]"===q(a)}function l(a){return"object"==typeof a&&null!==a}function m(a){return"[object Date]"===q(a)}function n(a){return"[object Error]"===q(a)||a instanceof Error}function o(a){return"function"==typeof a}function p(a){return null===a||"boolean"==typeof a||"number"==typeof a||"string"==typeof a||"symbol"==typeof a||"undefined"==typeof a}function q(a){return Object.prototype.toString.call(a)}c.isArray=b,c.isBoolean=d,c.isNull=e,c.isNullOrUndefined=f,c.isNumber=g,c.isString=h,c.isSymbol=i,c.isUndefined=j,c.isRegExp=k,c.isObject=l,c.isDate=m,c.isError=n,c.isFunction=o,c.isPrimitive=p,c.isBuffer=a.isBuffer}).call(this,{isBuffer:a("../../is-buffer/index.js")})},{"../../is-buffer/index.js":23}],20:[function(a,b,c){function d(){this._events=this._events||{},this._maxListeners=this._maxListeners||void 0}function e(a){return"function"==typeof a}function f(a){return"number"==typeof a}function g(a){return"object"==typeof a&&null!==a}function h(a){return void 0===a}b.exports=d,d.EventEmitter=d,d.prototype._events=void 0,d.prototype._maxListeners=void 0,d.defaultMaxListeners=10,d.prototype.setMaxListeners=function(a){if(!f(a)||0>a||isNaN(a))throw TypeError("n must be a positive number");return this._maxListeners=a,this},d.prototype.emit=function(a){var b,c,d,f,i,j;if(this._events||(this._events={}),"error"===a&&(!this._events.error||g(this._events.error)&&!this._events.error.length)){if(b=arguments[1],b instanceof Error)throw b;var k=new Error('Uncaught, unspecified "error" event. ('+b+")");throw k.context=b,k}if(c=this._events[a],h(c))return!1;if(e(c))switch(arguments.length){case 1:c.call(this);break;case 2:c.call(this,arguments[1]);break;case 3:c.call(this,arguments[1],arguments[2]);break;default:f=Array.prototype.slice.call(arguments,1),c.apply(this,f)}else if(g(c))for(f=Array.prototype.slice.call(arguments,1),j=c.slice(),d=j.length,i=0;d>i;i++)j[i].apply(this,f);return!0},d.prototype.addListener=function(a,b){var c;if(!e(b))throw TypeError("listener must be a function");return this._events||(this._events={}),this._events.newListener&&this.emit("newListener",a,e(b.listener)?b.listener:b),this._events[a]?g(this._events[a])?this._events[a].push(b):this._events[a]=[this._events[a],b]:this._events[a]=b,g(this._events[a])&&!this._events[a].warned&&(c=h(this._maxListeners)?d.defaultMaxListeners:this._maxListeners,c&&c>0&&this._events[a].length>c&&(this._events[a].warned=!0,console.error("(node) warning: possible EventEmitter memory leak detected. %d listeners added. Use emitter.setMaxListeners() to increase limit.",this._events[a].length),"function"==typeof console.trace&&console.trace())),this},d.prototype.on=d.prototype.addListener,d.prototype.once=function(a,b){function c(){this.removeListener(a,c),d||(d=!0,b.apply(this,arguments))}if(!e(b))throw TypeError("listener must be a function");var d=!1;return c.listener=b,this.on(a,c),this},d.prototype.removeListener=function(a,b){var c,d,f,h;if(!e(b))throw TypeError("listener must be a function");if(!this._events||!this._events[a])return this;if(c=this._events[a],f=c.length,d=-1,c===b||e(c.listener)&&c.listener===b)delete this._events[a],this._events.removeListener&&this.emit("removeListener",a,b);else if(g(c)){for(h=f;h-->0;)if(c[h]===b||c[h].listener&&c[h].listener===b){d=h;break}if(0>d)return this;1===c.length?(c.length=0,delete this._events[a]):c.splice(d,1),this._events.removeListener&&this.emit("removeListener",a,b)}return this},d.prototype.removeAllListeners=function(a){var b,c;if(!this._events)return this;if(!this._events.removeListener)return 0===arguments.length?this._events={}:this._events[a]&&delete this._events[a],this;if(0===arguments.length){for(b in this._events)"removeListener"!==b&&this.removeAllListeners(b);return this.removeAllListeners("removeListener"),this._events={},this}if(c=this._events[a],e(c))this.removeListener(a,c);else if(c)for(;c.length;)this.removeListener(a,c[c.length-1]);return delete this._events[a],this},d.prototype.listeners=function(a){var b;return b=this._events&&this._events[a]?e(this._events[a])?[this._events[a]]:this._events[a].slice():[]},d.prototype.listenerCount=function(a){if(this._events){var b=this._events[a];if(e(b))return 1;if(b)return b.length}return 0},d.listenerCount=function(a,b){return a.listenerCount(b)}},{}],21:[function(a,b,c){c.read=function(a,b,c,d,e){var f,g,h=8*e-d-1,i=(1<>1,k=-7,l=c?e-1:0,m=c?-1:1,n=a[b+l];for(l+=m,f=n&(1<<-k)-1,n>>=-k,k+=h;k>0;f=256*f+a[b+l],l+=m,k-=8);for(g=f&(1<<-k)-1,f>>=-k,k+=d;k>0;g=256*g+a[b+l],l+=m,k-=8);if(0===f)f=1-j;else{if(f===i)return g?NaN:(n?-1:1)*(1/0);g+=Math.pow(2,d),f-=j}return(n?-1:1)*g*Math.pow(2,f-d)},c.write=function(a,b,c,d,e,f){var g,h,i,j=8*f-e-1,k=(1<>1,m=23===e?Math.pow(2,-24)-Math.pow(2,-77):0,n=d?0:f-1,o=d?1:-1,p=0>b||0===b&&0>1/b?1:0;for(b=Math.abs(b),isNaN(b)||b===1/0?(h=isNaN(b)?1:0,g=k):(g=Math.floor(Math.log(b)/Math.LN2),b*(i=Math.pow(2,-g))<1&&(g--,i*=2),b+=g+l>=1?m/i:m*Math.pow(2,1-l),b*i>=2&&(g++,i/=2),g+l>=k?(h=0,g=k):g+l>=1?(h=(b*i-1)*Math.pow(2,e),g+=l):(h=b*Math.pow(2,l-1)*Math.pow(2,e),g=0));e>=8;a[c+n]=255&h,n+=o,h/=256,e-=8);for(g=g<0;a[c+n]=255&g,n+=o,g/=256,j-=8);a[c+n-o]|=128*p}},{}],22:[function(a,b,c){"function"==typeof Object.create?b.exports=function(a,b){a.super_=b,a.prototype=Object.create(b.prototype,{constructor:{value:a,enumerable:!1,writable:!0,configurable:!0}})}:b.exports=function(a,b){a.super_=b;var c=function(){};c.prototype=b.prototype,a.prototype=new c,a.prototype.constructor=a}},{}],23:[function(a,b,c){function d(a){return!!a.constructor&&"function"==typeof a.constructor.isBuffer&&a.constructor.isBuffer(a)}function e(a){return"function"==typeof a.readFloatLE&&"function"==typeof a.slice&&d(a.slice(0,0))}b.exports=function(a){return null!=a&&(d(a)||e(a)||!!a._isBuffer)}},{}],24:[function(a,b,c){var d={}.toString;b.exports=Array.isArray||function(a){return"[object Array]"==d.call(a)}},{}],25:[function(a,b,c){(function(a){"use strict";function c(b,c,d,e){if("function"!=typeof b)throw new TypeError('"callback" argument must be a function');var f,g,h=arguments.length;switch(h){case 0:case 1:return a.nextTick(b);case 2:return a.nextTick(function(){b.call(null,c)});case 3:return a.nextTick(function(){b.call(null,c,d)});case 4:return a.nextTick(function(){b.call(null,c,d,e)});default:for(f=new Array(h-1),g=0;g1)for(var c=1;c0)if(b.ended&&!e){var g=new Error("stream.push() after EOF");a.emit("error",g)}else if(b.endEmitted&&e){var i=new Error("stream.unshift() after end event");a.emit("error",i)}else{var j;!b.decoder||e||d||(c=b.decoder.write(c),j=!b.objectMode&&0===c.length),e||(b.reading=!1),j||(b.flowing&&0===b.length&&!b.sync?(a.emit("data",c),a.read(0)):(b.length+=b.objectMode?1:c.length,e?b.buffer.unshift(c):b.buffer.push(c),b.needReadable&&m(a))),o(a,b)}else e||(b.reading=!1);return h(b)}function h(a){return!a.ended&&(a.needReadable||a.length=P?a=P:(a--,a|=a>>>1,a|=a>>>2,a|=a>>>4,a|=a>>>8,a|=a>>>16,a++),a}function j(a,b){return 0>=a||0===b.length&&b.ended?0:b.objectMode?1:a!==a?b.flowing&&b.length?b.buffer.head.data.length:b.length:(a>b.highWaterMark&&(b.highWaterMark=i(a)),a<=b.length?a:b.ended?b.length:(b.needReadable=!0,0))}function k(a,b){var c=null;return H.isBuffer(b)||"string"==typeof b||null===b||void 0===b||a.objectMode||(c=new TypeError("Invalid non-string/buffer chunk")),c}function l(a,b){if(!b.ended){if(b.decoder){var c=b.decoder.end();c&&c.length&&(b.buffer.push(c),b.length+=b.objectMode?1:c.length)}b.ended=!0,m(a)}}function m(a){var b=a._readableState;b.needReadable=!1,b.emittedReadable||(L("emitReadable",b.flowing),b.emittedReadable=!0,b.sync?D(n,a):n(a))}function n(a){L("emit readable"),a.emit("readable"),u(a)}function o(a,b){b.readingMore||(b.readingMore=!0,D(p,a,b))}function p(a,b){for(var c=b.length;!b.reading&&!b.flowing&&!b.ended&&b.length=b.length?(c=b.decoder?b.buffer.join(""):1===b.buffer.length?b.buffer.head.data:b.buffer.concat(b.length),b.buffer.clear()):c=w(a,b.buffer,b.decoder),c}function w(a,b,c){var d;return af.length?f.length:a;if(e+=g===f.length?f:f.slice(0,a),a-=g,0===a){g===f.length?(++d,c.next?b.head=c.next:b.head=b.tail=null):(b.head=c,c.data=f.slice(g));break}++d}return b.length-=d,e}function y(a,b){var c=I.allocUnsafe(a),d=b.head,e=1;for(d.data.copy(c),a-=d.data.length;d=d.next;){var f=d.data,g=a>f.length?f.length:a;if(f.copy(c,c.length-a,0,g),a-=g,0===a){g===f.length?(++e,d.next?b.head=d.next:b.head=b.tail=null):(b.head=d,d.data=f.slice(g));break}++e}return b.length-=e,c}function z(a){var b=a._readableState;if(b.length>0)throw new Error('"endReadable()" called on non-empty stream');b.endEmitted||(b.ended=!0,D(A,b,a))}function A(a,b){a.endEmitted||0!==a.length||(a.endEmitted=!0,b.readable=!1,b.emit("end"))}function B(a,b){for(var c=0,d=a.length;d>c;c++)b(a[c],c)}function C(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1}b.exports=f;var D=a("process-nextick-args"),E=a("isarray");f.ReadableState=e;var F,G=(a("events").EventEmitter,function(a,b){return a.listeners(b).length});!function(){try{F=a("stream")}catch(b){}finally{F||(F=a("events").EventEmitter)}}();var H=a("buffer").Buffer,I=a("buffer-shims"),J=a("core-util-is");J.inherits=a("inherits");var K=a("util"),L=void 0;L=K&&K.debuglog?K.debuglog("stream"):function(){};var M,N=a("./internal/streams/BufferList");J.inherits(f,F);var O,O;f.prototype.push=function(a,b){var c=this._readableState;return c.objectMode||"string"!=typeof a||(b=b||c.defaultEncoding,b!==c.encoding&&(a=I.from(a,b),b="")),g(this,c,a,b,!1)},f.prototype.unshift=function(a){var b=this._readableState;return g(this,b,a,"",!0)},f.prototype.isPaused=function(){return this._readableState.flowing===!1},f.prototype.setEncoding=function(b){return M||(M=a("string_decoder/").StringDecoder),this._readableState.decoder=new M(b),this._readableState.encoding=b,this};var P=8388608;f.prototype.read=function(a){L("read",a),a=parseInt(a,10);var b=this._readableState,c=a;if(0!==a&&(b.emittedReadable=!1),0===a&&b.needReadable&&(b.length>=b.highWaterMark||b.ended))return L("read: emitReadable",b.length,b.ended),0===b.length&&b.ended?z(this):m(this),null;if(a=j(a,b),0===a&&b.ended)return 0===b.length&&z(this),null;var d=b.needReadable;L("need readable",d),(0===b.length||b.length-a0?v(a,b):null,null===e?(b.needReadable=!0,a=0):b.length-=a,0===b.length&&(b.ended||(b.needReadable=!0),c!==a&&b.ended&&z(this)),null!==e&&this.emit("data",e),e},f.prototype._read=function(a){this.emit("error",new Error("not implemented"))},f.prototype.pipe=function(a,b){function e(a){L("onunpipe"),a===m&&g()}function f(){L("onend"),a.end()}function g(){L("cleanup"),a.removeListener("close",j),a.removeListener("finish",k),a.removeListener("drain",r),a.removeListener("error",i),a.removeListener("unpipe",e),m.removeListener("end",f),m.removeListener("end",g),m.removeListener("data",h),s=!0,!n.awaitDrain||a._writableState&&!a._writableState.needDrain||r()}function h(b){L("ondata"),t=!1;var c=a.write(b);!1!==c||t||((1===n.pipesCount&&n.pipes===a||n.pipesCount>1&&-1!==C(n.pipes,a))&&!s&&(L("false write response, pause",m._readableState.awaitDrain),m._readableState.awaitDrain++,t=!0),m.pause())}function i(b){L("onerror",b),l(),a.removeListener("error",i),0===G(a,"error")&&a.emit("error",b)}function j(){a.removeListener("finish",k),l()}function k(){L("onfinish"),a.removeListener("close",j),l()}function l(){L("unpipe"),m.unpipe(a)}var m=this,n=this._readableState;switch(n.pipesCount){case 0:n.pipes=a;break;case 1:n.pipes=[n.pipes,a];break;default:n.pipes.push(a)}n.pipesCount+=1,L("pipe count=%d opts=%j",n.pipesCount,b);var o=(!b||b.end!==!1)&&a!==c.stdout&&a!==c.stderr,p=o?f:g;n.endEmitted?D(p):m.once("end",p),a.on("unpipe",e);var r=q(m);a.on("drain",r);var s=!1,t=!1;return m.on("data",h),d(a,"error",i),a.once("close",j),a.once("finish",k),a.emit("pipe",m),n.flowing||(L("pipe resume"),m.resume()),a},f.prototype.unpipe=function(a){var b=this._readableState;if(0===b.pipesCount)return this;if(1===b.pipesCount)return a&&a!==b.pipes?this:(a||(a=b.pipes),b.pipes=null,b.pipesCount=0,b.flowing=!1,a&&a.emit("unpipe",this),this);if(!a){var c=b.pipes,d=b.pipesCount;b.pipes=null,b.pipesCount=0,b.flowing=!1;for(var e=0;d>e;e++)c[e].emit("unpipe",this);return this}var f=C(b.pipes,a);return-1===f?this:(b.pipes.splice(f,1),b.pipesCount-=1,1===b.pipesCount&&(b.pipes=b.pipes[0]),a.emit("unpipe",this),this)},f.prototype.on=function(a,b){var c=F.prototype.on.call(this,a,b);if("data"===a)this._readableState.flowing!==!1&&this.resume();else if("readable"===a){var d=this._readableState;d.endEmitted||d.readableListening||(d.readableListening=d.needReadable=!0,d.emittedReadable=!1,d.reading?d.length&&m(this,d):D(r,this))}return c},f.prototype.addListener=f.prototype.on,f.prototype.resume=function(){var a=this._readableState;return a.flowing||(L("resume"),a.flowing=!0,s(this,a)),this},f.prototype.pause=function(){return L("call pause flowing=%j",this._readableState.flowing),!1!==this._readableState.flowing&&(L("pause"),this._readableState.flowing=!1,this.emit("pause")),this},f.prototype.wrap=function(a){var b=this._readableState,c=!1,d=this;a.on("end",function(){if(L("wrapped end"),b.decoder&&!b.ended){var a=b.decoder.end();a&&a.length&&d.push(a)}d.push(null)}),a.on("data",function(e){if(L("wrapped data"),b.decoder&&(e=b.decoder.write(e)),(!b.objectMode||null!==e&&void 0!==e)&&(b.objectMode||e&&e.length)){var f=d.push(e);f||(c=!0,a.pause())}});for(var e in a)void 0===this[e]&&"function"==typeof a[e]&&(this[e]=function(b){return function(){return a[b].apply(a,arguments)}}(e));var f=["error","close","destroy","pause","resume"];return B(f,function(b){a.on(b,d.emit.bind(d,b))}),d._read=function(b){L("wrapped _read",b),c&&(c=!1,a.resume())},d},f._fromList=v}).call(this,a("_process"))},{"./_stream_duplex":28,"./internal/streams/BufferList":33,_process:26,buffer:18,"buffer-shims":17,"core-util-is":19,events:20,inherits:22,isarray:24,"process-nextick-args":25,"string_decoder/":39,util:16}],31:[function(a,b,c){"use strict";function d(a){this.afterTransform=function(b,c){return e(a,b,c)},this.needTransform=!1,this.transforming=!1,this.writecb=null,this.writechunk=null,this.writeencoding=null}function e(a,b,c){var d=a._transformState;d.transforming=!1;var e=d.writecb;if(!e)return a.emit("error",new Error("no writecb in Transform class"));d.writechunk=null,d.writecb=null,null!==c&&void 0!==c&&a.push(c),e(b);var f=a._readableState;f.reading=!1,(f.needReadable||f.length-1?setImmediate:x;g.WritableState=f;var z=a("core-util-is");z.inherits=a("inherits");var A,B={deprecate:a("util-deprecate")};!function(){try{A=a("stream")}catch(b){}finally{A||(A=a("events").EventEmitter)}}();var C=a("buffer").Buffer,D=a("buffer-shims");z.inherits(g,A);var E;f.prototype.getBuffer=function(){for(var a=this.bufferedRequest,b=[];a;)b.push(a),a=a.next;return b},function(){try{Object.defineProperty(f.prototype,"buffer",{get:B.deprecate(function(){return this.getBuffer()},"_writableState.buffer is deprecated. Use _writableState.getBuffer instead.")})}catch(a){}}();var E;g.prototype.pipe=function(){this.emit("error",new Error("Cannot pipe, not readable"))},g.prototype.write=function(a,b,c){var e=this._writableState,f=!1;return"function"==typeof b&&(c=b,b=null),C.isBuffer(a)?b="buffer":b||(b=e.defaultEncoding),"function"!=typeof c&&(c=d),e.ended?h(this,c):i(this,e,a,c)&&(e.pendingcb++,f=k(this,e,a,b,c)),f},g.prototype.cork=function(){var a=this._writableState;a.corked++},g.prototype.uncork=function(){var a=this._writableState;a.corked&&(a.corked--,a.writing||a.corked||a.finished||a.bufferProcessing||!a.bufferedRequest||r(this,a))},g.prototype.setDefaultEncoding=function(a){if("string"==typeof a&&(a=a.toLowerCase()),!(["hex","utf8","utf-8","ascii","binary","base64","ucs2","ucs-2","utf16le","utf-16le","raw"].indexOf((a+"").toLowerCase())>-1))throw new TypeError("Unknown encoding: "+a);return this._writableState.defaultEncoding=a,this},g.prototype._write=function(a,b,c){c(new Error("not implemented"))},g.prototype._writev=null,g.prototype.end=function(a,b,c){var d=this._writableState;"function"==typeof a?(c=a,a=null,b=null):"function"==typeof b&&(c=b,b=null),null!==a&&void 0!==a&&this.write(a,b),d.corked&&(d.corked=1,this.uncork()),d.ending||d.finished||v(this,d,c)}}).call(this,a("_process"))},{"./_stream_duplex":28,_process:26,buffer:18,"buffer-shims":17,"core-util-is":19,events:20,inherits:22,"process-nextick-args":25,"util-deprecate":40}],33:[function(a,b,c){"use strict";function d(){this.head=null,this.tail=null,this.length=0}var e=(a("buffer").Buffer,a("buffer-shims"));b.exports=d,d.prototype.push=function(a){var b={data:a,next:null};this.length>0?this.tail.next=b:this.head=b,this.tail=b,++this.length},d.prototype.unshift=function(a){var b={data:a,next:this.head};0===this.length&&(this.tail=b),this.head=b,++this.length},d.prototype.shift=function(){if(0!==this.length){var a=this.head.data;return 1===this.length?this.head=this.tail=null:this.head=this.head.next,--this.length,a}},d.prototype.clear=function(){this.head=this.tail=null,this.length=0},d.prototype.join=function(a){if(0===this.length)return"";for(var b=this.head,c=""+b.data;b=b.next;)c+=a+b.data;return c},d.prototype.concat=function(a){if(0===this.length)return e.alloc(0);if(1===this.length)return this.head.data;for(var b=e.allocUnsafe(a>>>0),c=this.head,d=0;c;)c.data.copy(b,d),d+=c.data.length,c=c.next;return b}},{buffer:18,"buffer-shims":17}],34:[function(a,b,c){b.exports=a("./lib/_stream_passthrough.js")},{"./lib/_stream_passthrough.js":29}],35:[function(a,b,c){(function(d){var e=function(){try{return a("stream")}catch(b){}}();c=b.exports=a("./lib/_stream_readable.js"),c.Stream=e||c,c.Readable=c,c.Writable=a("./lib/_stream_writable.js"),c.Duplex=a("./lib/_stream_duplex.js"),c.Transform=a("./lib/_stream_transform.js"),c.PassThrough=a("./lib/_stream_passthrough.js"),!d.browser&&"disable"===d.env.READABLE_STREAM&&e&&(b.exports=e)}).call(this,a("_process"))},{"./lib/_stream_duplex.js":28,"./lib/_stream_passthrough.js":29,"./lib/_stream_readable.js":30,"./lib/_stream_transform.js":31,"./lib/_stream_writable.js":32,_process:26}],36:[function(a,b,c){b.exports=a("./lib/_stream_transform.js")},{"./lib/_stream_transform.js":31}],37:[function(a,b,c){b.exports=a("./lib/_stream_writable.js")},{"./lib/_stream_writable.js":32}],38:[function(a,b,c){function d(){e.call(this)}b.exports=d;var e=a("events").EventEmitter,f=a("inherits");f(d,e),d.Readable=a("readable-stream/readable.js"),d.Writable=a("readable-stream/writable.js"),d.Duplex=a("readable-stream/duplex.js"),d.Transform=a("readable-stream/transform.js"),d.PassThrough=a("readable-stream/passthrough.js"),d.Stream=d,d.prototype.pipe=function(a,b){function c(b){a.writable&&!1===a.write(b)&&j.pause&&j.pause()}function d(){j.readable&&j.resume&&j.resume()}function f(){k||(k=!0,a.end())}function g(){k||(k=!0,"function"==typeof a.destroy&&a.destroy())}function h(a){if(i(),0===e.listenerCount(this,"error"))throw a}function i(){j.removeListener("data",c),a.removeListener("drain",d),j.removeListener("end",f),j.removeListener("close",g),j.removeListener("error",h),a.removeListener("error",h),j.removeListener("end",i),j.removeListener("close",i),a.removeListener("close",i)}var j=this;j.on("data",c),a.on("drain",d),a._isStdio||b&&b.end===!1||(j.on("end",f),j.on("close",g));var k=!1;return j.on("error",h),a.on("error",h),j.on("end",i),j.on("close",i),a.on("close",i),a.emit("pipe",j),a}},{events:20,inherits:22,"readable-stream/duplex.js":27,"readable-stream/passthrough.js":34,"readable-stream/readable.js":35,"readable-stream/transform.js":36,"readable-stream/writable.js":37}],39:[function(a,b,c){function d(a){if(a&&!i(a))throw new Error("Unknown encoding: "+a)}function e(a){return a.toString(this.encoding)}function f(a){this.charReceived=a.length%2,this.charLength=this.charReceived?2:0}function g(a){this.charReceived=a.length%3,this.charLength=this.charReceived?3:0}var h=a("buffer").Buffer,i=h.isEncoding||function(a){switch(a&&a.toLowerCase()){case"hex":case"utf8":case"utf-8":case"ascii":case"binary":case"base64":case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":case"raw":return!0;default:return!1}},j=c.StringDecoder=function(a){switch(this.encoding=(a||"utf8").toLowerCase().replace(/[-_]/,""),d(a),this.encoding){case"utf8":this.surrogateSize=3;break;case"ucs2":case"utf16le":this.surrogateSize=2,this.detectIncompleteChar=f;break;case"base64":this.surrogateSize=3,this.detectIncompleteChar=g;break;default:return void(this.write=e)}this.charBuffer=new h(6),this.charReceived=0,this.charLength=0};j.prototype.write=function(a){for(var b="";this.charLength;){var c=a.length>=this.charLength-this.charReceived?this.charLength-this.charReceived:a.length;if(a.copy(this.charBuffer,this.charReceived,0,c),this.charReceived+=c,this.charReceived=55296&&56319>=d)){if(this.charReceived=this.charLength=0,0===a.length)return b;break}this.charLength+=this.surrogateSize,b=""}this.detectIncompleteChar(a);var e=a.length;this.charLength&&(a.copy(this.charBuffer,0,a.length-this.charReceived,e),e-=this.charReceived),b+=a.toString(this.encoding,0,e);var e=b.length-1,d=b.charCodeAt(e);if(d>=55296&&56319>=d){var f=this.surrogateSize;return this.charLength+=f,this.charReceived+=f,this.charBuffer.copy(this.charBuffer,f,0,f),a.copy(this.charBuffer,0,0,f),b.substring(0,e)}return b},j.prototype.detectIncompleteChar=function(a){for(var b=a.length>=3?3:a.length;b>0;b--){var c=a[a.length-b];if(1==b&&c>>5==6){this.charLength=2;break}if(2>=b&&c>>4==14){this.charLength=3;break}if(3>=b&&c>>3==30){this.charLength=4;break}}this.charReceived=b},j.prototype.end=function(a){var b="";if(a&&a.length&&(b=this.write(a)),this.charReceived){var c=this.charReceived,d=this.charBuffer,e=this.encoding;b+=d.slice(0,c).toString(e)}return b}},{buffer:18}],40:[function(a,b,c){(function(a){function c(a,b){function c(){if(!e){if(d("throwDeprecation"))throw new Error(b);d("traceDeprecation")?console.trace(b):console.warn(b),e=!0}return a.apply(this,arguments)}if(d("noDeprecation"))return a;var e=!1;return c}function d(b){try{if(!a.localStorage)return!1}catch(c){return!1}var d=a.localStorage[b];return null==d?!1:"true"===String(d).toLowerCase()}b.exports=c}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}]},{},[1,2,3,4,5,6,7,8,9,10,11,12,13,14]); \ No newline at end of file +/** + * Modules in this bundle + * @license + * + * brain.js: + * license: MIT (http://opensource.org/licenses/MIT) + * author: Heather Arthur + * homepage: https://github.com/harthur-org/brain.js#readme + * version: 1.0.0 + * + * base64-js: + * license: MIT (http://opensource.org/licenses/MIT) + * author: T. Jameson Little + * maintainers: beatgammit , feross + * homepage: https://github.com/beatgammit/base64-js#readme + * version: 1.1.2 + * + * buffer: + * license: MIT (http://opensource.org/licenses/MIT) + * author: Feross Aboukhadijeh + * maintainers: feross + * contributors: Romain Beauxis , James Halliday + * homepage: https://github.com/feross/buffer + * version: 4.9.1 + * + * buffer-shims: + * license: MIT (http://opensource.org/licenses/MIT) + * maintainers: cwmma + * homepage: https://github.com/calvinmetcalf/buffer-shims#readme + * version: 1.0.0 + * + * core-util-is: + * license: MIT (http://opensource.org/licenses/MIT) + * author: Isaac Z. Schlueter + * maintainers: isaacs + * homepage: https://github.com/isaacs/core-util-is#readme + * version: 1.0.2 + * + * events: + * license: MIT (http://opensource.org/licenses/MIT) + * author: Irakli Gozalishvili + * maintainers: gozala , defunctzombie + * homepage: https://github.com/Gozala/events#readme + * version: 1.1.1 + * + * ieee754: + * license: MIT (http://opensource.org/licenses/MIT) + * author: Feross Aboukhadijeh + * maintainers: feross + * contributors: Romain Beauxis + * homepage: https://github.com/feross/ieee754#readme + * version: 1.1.6 + * + * inherits: + * license: ISC (http://opensource.org/licenses/ISC) + * maintainers: isaacs + * homepage: https://github.com/isaacs/inherits#readme + * version: 2.0.2 + * + * isarray: + * license: MIT (http://opensource.org/licenses/MIT) + * author: Julian Gruber + * maintainers: juliangruber + * homepage: https://github.com/juliangruber/isarray + * version: 1.0.0 + * + * process: + * license: MIT (http://opensource.org/licenses/MIT) + * author: Roman Shtylman + * maintainers: coolaj86 , cwmma , defunctzombie + * homepage: https://github.com/shtylman/node-process#readme + * version: 0.11.9 + * + * process-nextick-args: + * license: MIT (http://opensource.org/licenses/MIT) + * maintainers: cwmma + * homepage: https://github.com/calvinmetcalf/process-nextick-args + * version: 1.0.7 + * + * readable-stream: + * license: MIT (http://opensource.org/licenses/MIT) + * maintainers: isaacs , tootallnate , rvagg , cwmma + * homepage: https://github.com/nodejs/readable-stream#readme + * version: 2.1.5 + * + * stream-browserify: + * license: MIT (http://opensource.org/licenses/MIT) + * author: James Halliday + * maintainers: substack , feross , stevemao + * homepage: https://github.com/substack/stream-browserify + * version: 2.0.1 + * + * string_decoder: + * license: MIT (http://opensource.org/licenses/MIT) + * maintainers: substack , rvagg + * homepage: https://github.com/rvagg/string_decoder + * version: 0.10.31 + * + * util-deprecate: + * license: MIT (http://opensource.org/licenses/MIT) + * author: Nathan Rajlich + * maintainers: tootallnate + * homepage: https://github.com/TooTallNate/util-deprecate + * version: 1.0.2 + * + * This header is generated by licensify (https://github.com/twada/licensify) + */ +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o0;e--){var r=Math.floor(Math.random()*(e+1)),s=t[e];t[e]=t[r],t[r]=s}return t}function crossValidate(t,e,r,s,a){a=a||4;var i=e.length/a;e.constructor===Array?shuffleArray(e):!function(){var t={};shuffleArray(Object.keys(e)).forEach(function(r){t[r]=e[r]}),e=t}();for(var o={error:0,trainTime:0,testTime:0,iterations:0,trainError:0},n={truePos:0,trueNeg:0,falsePos:0,falseNeg:0,total:0},f=[],l=[],u=void 0,c=void 0,h=0;hi&&(t=n,i=u)}return t}Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=likely; + +},{}],3:[function(require,module,exports){ +"use strict";function _classCallCheck(r,e){if(!(r instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(exports,"__esModule",{value:!0});var _createClass=function(){function r(r,e){for(var n=0;n0;)e[r[o]]=n++;return e}}]),r}();exports.default=lookup; + +},{}],4:[function(require,module,exports){ +"use strict";function _interopRequireDefault(t){return t&&t.__esModule?t:{default:t}}function _classCallCheck(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(exports,"__esModule",{value:!0});var _createClass=function(){function t(t,e){for(var r=0;r0){this.biases[r]=(0,_randos2.default)(i),e||(this.weights[r]=new Array(i)),this.changes[r]=new Array(i);for(var u=0;ui;v++){for(var d=0,g=0;g=0;e--)for(var r=0;re.binaryThresh?1:0,c=p[0]):(f=l.indexOf((0,_max2.default)(l)),c=p.indexOf((0,_max2.default)(p))),f!=c){var v=t[h];Object.assign(v,{actual:f,expected:c}),o.push(v)}r&&(0==f&&0==c?a++:1==f&&1==c?s++:0==f&&1==c?u++:1==f&&0==c&&i++);var d=l.map(function(t,e){return p[e]-t});n+=(0,_mse2.default)(d)},l=0;l0){t[e][u].bias=this.biases[e][i],t[e][u].weights={};for(var s in t[e-1]){var a=s;1==e&&this.inputLookup&&(a=this.inputLookup[s]),t[e][u].weights[s]=this.weights[e][i][a]}}}}return{layers:t,outputLookup:!!this.outputLookup,inputLookup:!!this.inputLookup}}},{key:"fromJSON",value:function(t){var e=t.layers.length;this.outputLayer=e-1,this.sizes=new Array(e),this.weights=new Array(e),this.biases=new Array(e),this.outputs=new Array(e);for(var r=0;r<=this.outputLayer;r++){var i=t.layers[r];0!=r||i[0]&&!t.inputLookup?r!=this.outputLayer||i[0]&&!t.outputLookup||(this.outputLookup=_lookup2.default.lookupFromHash(i)):this.inputLookup=_lookup2.default.lookupFromHash(i);var u=Object.keys(i);this.sizes[r]=u.length,this.weights[r]=[],this.biases[r]=[],this.outputs[r]=[];for(var s in u){var a=u[s];this.biases[r][s]=i[a].bias,this.weights[r][s]=(0,_toArray2.default)(i[a].weights)}}return this}},{key:"toFunction",value:function(){var t=this.toJSON(),e=JSON.stringify(t);return new Function("input","\n var net = "+e+";\n for (var i = 1; i < net.layers.length; i++) {\n var layer = net.layers[i];\n var output = {};\n \n for (var id in layer) {\n var node = layer[id];\n var sum = node.bias;\n \n for (var iid in node.weights) {\n sum += node.weights[iid] * input[iid];\n }\n output[id] = (1 / (1 + Math.exp(-sum)));\n }\n input = output;\n }\n return output;\n ")}},{key:"createTrainStream",value:function(t){return t=t||{},t.neuralNetwork=this,this.trainStream=new _trainStream2.default(t),this.trainStream}}]),t}();exports.default=NeuralNetwork; + +},{"./lookup":3,"./train-stream":32,"./utilities/max":33,"./utilities/mse":34,"./utilities/randos":38,"./utilities/range":39,"./utilities/to-array":40,"./utilities/zeros":42}],5:[function(require,module,exports){ +"use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function _classCallCheck(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function _possibleConstructorReturn(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function _inherits(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(exports,"__esModule",{value:!0});var _createClass=function(){function e(e,t){for(var r=0;r0;)r=this.states[t],r.hasOwnProperty("backpropagationFn")&&r.backpropagationFn(r.product,r.left,r.right);return r.product}}]),e}();exports.default=Equation; + +},{"./":13,"./add":8,"./add-b":7,"./all-ones":9,"./clone-negative":10,"./copy":11,"./multiply":18,"./multiply-b":15,"./multiply-element":17,"./multiply-element-b":16,"./ones-matrix":19,"./relu":22,"./relu-b":21,"./row-pluck":24,"./row-pluck-b":23,"./sigmoid":27,"./sigmoid-b":26,"./tanh":30,"./tanh-b":29}],13:[function(require,module,exports){ +"use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function _classCallCheck(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(exports,"__esModule",{value:!0});var _createClass=function(){function e(e,t){for(var r=0;r=this.weights.length)throw new Error("get accessor is skewed");return this.weights[r]}},{key:"setWeight",value:function(e,t,r){var s=this.columns*e+t;if(s<0&&s>=this.weights.length)throw new Error("set accessor is skewed");this.weights[s]=r}},{key:"setRecurrence",value:function(e,t,r){var s=this.columns*e+t;if(s<0&&s>=this.weights.length)throw new Error("set accessor is skewed");this.recurrence[s]=r}},{key:"toJSON",value:function(){return{rows:this.rows,columns:this.columns,weights:this.weights.slice(0)}}}],[{key:"fromJSON",value:function(t){for(var r=new e(t.rows,t.columns),s=0,n=t.rows*t.columns;s0?e.recurrence[t]:0}Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=reluB; + +},{}],22:[function(require,module,exports){ +"use strict";function relu(e,t){for(var r=0,s=t.weights.length;re)return o;o++}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=sampleI;var _random=require("../../utilities/random"),randomF=_random.randomF; + +},{"../../utilities/random":37}],26:[function(require,module,exports){ +"use strict";function sigmoidB(e,r){for(var t=0,c=e.recurrence.length;tr&&(r=e.weights[i]);var s=0;for(i=0;i0?t.equationConnections[t.equationConnections.length-1]:e.map(function(t){return new _matrix2.default(e[0],1)}),o=this.getEquation(r,r.inputMatrixToRow(t.input),a[0],n[0]);i.push(o);for(var u=1,s=e.length;u0;)r[e].runBackpropagate(t[e-1]+1),e--;r[0].runBackpropagate(0)}},{key:"step",value:function(){for(var t=this.learningRate,e=this.regc,n=this.clipval,r=this.model,i=0,a=0,o=r.allMatrices,u=o.length,s=0;sr.outputMatrixIndex)for(var p=0,f=l.weights.length;pn&&(m=n,i++),m<-n&&(m=-n,i++),a++,l.weights[d]=l.weights[d]+-t*m/Math.sqrt(h.weights[d]+this.smoothEps)-e*l.weights[d],l.recurrence[d]=0}}this.ratioClipped=i/a}},{key:"predict",value:function(){for(var t=arguments.length<=0||void 0===arguments[0]?[]:arguments[0],e=arguments.length<=1||void 0===arguments[1]?100:arguments[1],n=!(arguments.length<=2||void 0===arguments[2])&&arguments[2],r=arguments.length<=3||void 0===arguments[3]?1:arguments[3],i=this.model,a=void 0,o=0;i.equations.length=e)break;t.push(u)}return t.map(function(t){return t-1})}},{key:"runInput",value:function(t){this.outputs[0]=t;for(var e=null,n=1;n<=this.outputLayer;n++){for(var r=0;r-1)return"typeof prevStates["+o+"] === 'object' ? prevStates["+o+"].product : new Matrix("+t.rows+", "+t.columns+")";case a.right:if(o>-1)return"typeof prevStates["+o+"] === 'object' ? prevStates["+o+"].product : new Matrix("+t.rows+", "+t.columns+")";case a.product:return"new Matrix("+t.rows+", "+t.columns+")";default:throw Error("unknown state")}}if(t===a.product)return"states["+r+"].product";if(t===a.right)return"states["+r+"].right";if(t===a.left)return"states["+r+"].left"}}function e(t){for(var e=a.equationConnections[0],n=o[0].states,r=0,i=n.length;r= maxPredictionLength) {\n // something is wrong\n break;\n }\n\n result.push(ix);\n }\n\n return result.map(function(value) { return value - 1; });\n \n function Matrix(rows, columns) {\n this.rows = rows;\n this.columns = columns;\n this.weights = zeros(rows * columns);\n this.recurrence = zeros(rows * columns);\n }\n "+_zeros2.default.toString()+"\n "+_softmax2.default.toString()+"\n "+_random.randomF.toString()+"\n "+_sampleI3.default.toString()+"\n "+_maxI2.default.toString())}}]),t}();exports.default=RNN; + +},{"../utilities/random":37,"../utilities/zeros":42,"./matrix":13,"./matrix/copy":11,"./matrix/equation":12,"./matrix/max-i":14,"./matrix/random-matrix":20,"./matrix/sample-i":25,"./matrix/softmax":28}],32:[function(require,module,exports){ +"use strict";function _interopRequireDefault(t){return t&&t.__esModule?t:{default:t}}function _classCallCheck(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function _possibleConstructorReturn(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function _inherits(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}function uniques(t){for(var e=[],r=0,o=t.length;rthis.errorThresh){if("function"==typeof this.floodCallback)return this.floodCallback()}else if("function"==typeof this.doneTrainingCallback)return this.doneTrainingCallback({error:r,iterations:this.i})}}]),e}(_stream.Writable);exports.default=TrainStream; + +},{"./lookup":3,"stream":67}],33:[function(require,module,exports){ +"use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function max(e){return Math.max.apply(Math,(0,_toArray2.default)(e))}Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=max;var _toArray=require("./to-array"),_toArray2=_interopRequireDefault(_toArray); + +},{"./to-array":40}],34:[function(require,module,exports){ +"use strict";function mse(e){for(var t=0,r=0;r1)return gaussRandom();var o=Math.sqrt(-2*Math.log(n)/n);return gaussRandom.vVal=r*o,gaussRandom.returnV=!0,a*o}Object.defineProperty(exports,"__esModule",{value:!0}),exports.randomF=randomF,exports.randomI=randomI,exports.randomN=randomN,gaussRandom.returnV=!1,gaussRandom.vVal=0; + +},{}],38:[function(require,module,exports){ +"use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function randos(e){for(var r=new Array(e),t=0;t=t&&(this.indexTable[f]=h,this.characterTable[h]=f)}}return _createClass(r,[{key:"toIndexes",value:function(r){for(var e=arguments.length<=1||void 0===arguments[1]?0:arguments[1],t=[],a=this.indexTable,n=0,o=r.length;n0)throw new Error("Invalid string. Length must be a multiple of 4");n="="===o[a-2]?2:"="===o[a-1]?1:0,p=new Arr(3*a/4-n),t=n>0?a-4:a;var k=0;for(r=0,e=0;r>16&255,p[k++]=u>>8&255,p[k++]=255&u;return 2===n?(u=revLookup[o.charCodeAt(r)]<<2|revLookup[o.charCodeAt(r+1)]>>4,p[k++]=255&u):1===n&&(u=revLookup[o.charCodeAt(r)]<<10|revLookup[o.charCodeAt(r+1)]<<4|revLookup[o.charCodeAt(r+2)]>>2,p[k++]=u>>8&255,p[k++]=255&u),p}function tripletToBase64(o){return lookup[o>>18&63]+lookup[o>>12&63]+lookup[o>>6&63]+lookup[63&o]}function encodeChunk(o,r,e){for(var t,u=[],n=r;nk?k:a+p));return 1===t?(r=o[e-1],u+=lookup[r>>2],u+=lookup[r<<4&63],u+="=="):2===t&&(r=(o[e-2]<<8)+o[e-1],u+=lookup[r>>10],u+=lookup[r>>4&63],u+=lookup[r<<2&63],u+="="),n.push(u),n.join("")}exports.toByteArray=toByteArray,exports.fromByteArray=fromByteArray;var lookup=[],revLookup=[],Arr="undefined"!=typeof Uint8Array?Uint8Array:Array;init(); + +},{}],45:[function(require,module,exports){ + +},{}],46:[function(require,module,exports){ +(function (global){ +"use strict";var buffer=require("buffer"),Buffer=buffer.Buffer,SlowBuffer=buffer.SlowBuffer,MAX_LEN=buffer.kMaxLength||2147483647;exports.alloc=function(r,e,f){if("function"==typeof Buffer.alloc)return Buffer.alloc(r,e,f);if("number"==typeof f)throw new TypeError("encoding must not be number");if("number"!=typeof r)throw new TypeError("size must be a number");if(r>MAX_LEN)throw new RangeError("size is too large");var n=f,o=e;void 0===o&&(n=void 0,o=0);var t=new Buffer(r);if("string"==typeof o)for(var u=new Buffer(o,n),i=u.length,a=-1;++aMAX_LEN)throw new RangeError("size is too large");return new Buffer(r)},exports.from=function(r,e,f){if("function"==typeof Buffer.from&&(!global.Uint8Array||Uint8Array.from!==Buffer.from))return Buffer.from(r,e,f);if("number"==typeof r)throw new TypeError('"value" argument must not be a number');if("string"==typeof r)return new Buffer(r,e);if("undefined"!=typeof ArrayBuffer&&r instanceof ArrayBuffer){var n=e;if(1===arguments.length)return new Buffer(r);"undefined"==typeof n&&(n=0);var o=f;if("undefined"==typeof o&&(o=r.byteLength-n),n>=r.byteLength)throw new RangeError("'offset' is out of bounds");if(o>r.byteLength-n)throw new RangeError("'length' is out of bounds");return new Buffer(r.slice(n,n+o))}if(Buffer.isBuffer(r)){var t=new Buffer(r.length);return r.copy(t,0,0,r.length),t}if(r){if(Array.isArray(r)||"undefined"!=typeof ArrayBuffer&&r.buffer instanceof ArrayBuffer||"length"in r)return new Buffer(r);if("Buffer"===r.type&&Array.isArray(r.data))return new Buffer(r.data)}throw new TypeError("First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.")},exports.allocUnsafeSlow=function(r){if("function"==typeof Buffer.allocUnsafeSlow)return Buffer.allocUnsafeSlow(r);if("number"!=typeof r)throw new TypeError("size must be a number");if(r>=MAX_LEN)throw new RangeError("size is too large");return new SlowBuffer(r)}; + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"buffer":47}],47:[function(require,module,exports){ +(function (global){ +"use strict";function typedArraySupport(){try{var t=new Uint8Array(1);return t.__proto__={__proto__:Uint8Array.prototype,foo:function(){return 42}},42===t.foo()&&"function"==typeof t.subarray&&0===t.subarray(1,1).byteLength}catch(t){return!1}}function kMaxLength(){return Buffer.TYPED_ARRAY_SUPPORT?2147483647:1073741823}function createBuffer(t,e){if(kMaxLength()=kMaxLength())throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+kMaxLength().toString(16)+" bytes");return 0|t}function SlowBuffer(t){return+t!=t&&(t=0),Buffer.alloc(+t)}function byteLength(t,e){if(Buffer.isBuffer(t))return t.length;if("undefined"!=typeof ArrayBuffer&&"function"==typeof ArrayBuffer.isView&&(ArrayBuffer.isView(t)||t instanceof ArrayBuffer))return t.byteLength;"string"!=typeof t&&(t=""+t);var r=t.length;if(0===r)return 0;for(var n=!1;;)switch(e){case"ascii":case"latin1":case"binary":return r;case"utf8":case"utf-8":case void 0:return utf8ToBytes(t).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*r;case"hex":return r>>>1;case"base64":return base64ToBytes(t).length;default:if(n)return utf8ToBytes(t).length;e=(""+e).toLowerCase(),n=!0}}function slowToString(t,e,r){var n=!1;if((void 0===e||e<0)&&(e=0),e>this.length)return"";if((void 0===r||r>this.length)&&(r=this.length),r<=0)return"";if(r>>>=0,e>>>=0,r<=e)return"";for(t||(t="utf8");;)switch(t){case"hex":return hexSlice(this,e,r);case"utf8":case"utf-8":return utf8Slice(this,e,r);case"ascii":return asciiSlice(this,e,r);case"latin1":case"binary":return latin1Slice(this,e,r);case"base64":return base64Slice(this,e,r);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return utf16leSlice(this,e,r);default:if(n)throw new TypeError("Unknown encoding: "+t);t=(t+"").toLowerCase(),n=!0}}function swap(t,e,r){var n=t[e];t[e]=t[r],t[r]=n}function bidirectionalIndexOf(t,e,r,n,f){if(0===t.length)return-1;if("string"==typeof r?(n=r,r=0):r>2147483647?r=2147483647:r<-2147483648&&(r=-2147483648),r=+r,isNaN(r)&&(r=f?0:t.length-1),r<0&&(r=t.length+r),r>=t.length){if(f)return-1;r=t.length-1}else if(r<0){if(!f)return-1;r=0}if("string"==typeof e&&(e=Buffer.from(e,n)),Buffer.isBuffer(e))return 0===e.length?-1:arrayIndexOf(t,e,r,n,f);if("number"==typeof e)return e=255&e,Buffer.TYPED_ARRAY_SUPPORT&&"function"==typeof Uint8Array.prototype.indexOf?f?Uint8Array.prototype.indexOf.call(t,e,r):Uint8Array.prototype.lastIndexOf.call(t,e,r):arrayIndexOf(t,[e],r,n,f);throw new TypeError("val must be string, number or Buffer")}function arrayIndexOf(t,e,r,n,f){function i(t,e){return 1===o?t[e]:t.readUInt16BE(e*o)}var o=1,u=t.length,s=e.length;if(void 0!==n&&(n=String(n).toLowerCase(),"ucs2"===n||"ucs-2"===n||"utf16le"===n||"utf-16le"===n)){if(t.length<2||e.length<2)return-1;o=2,u/=2,s/=2,r/=2}var a;if(f){var h=-1;for(a=r;au&&(r=u-s),a=r;a>=0;a--){for(var c=!0,l=0;lf&&(n=f)):n=f;var i=e.length;if(i%2!==0)throw new TypeError("Invalid hex string");n>i/2&&(n=i/2);for(var o=0;o239?4:i>223?3:i>191?2:1;if(f+u<=r){var s,a,h,c;switch(u){case 1:i<128&&(o=i);break;case 2:s=t[f+1],128===(192&s)&&(c=(31&i)<<6|63&s,c>127&&(o=c));break;case 3:s=t[f+1],a=t[f+2],128===(192&s)&&128===(192&a)&&(c=(15&i)<<12|(63&s)<<6|63&a,c>2047&&(c<55296||c>57343)&&(o=c));break;case 4:s=t[f+1],a=t[f+2],h=t[f+3],128===(192&s)&&128===(192&a)&&128===(192&h)&&(c=(15&i)<<18|(63&s)<<12|(63&a)<<6|63&h,c>65535&&c<1114112&&(o=c))}}null===o?(o=65533,u=1):o>65535&&(o-=65536,n.push(o>>>10&1023|55296),o=56320|1023&o),n.push(o),f+=u}return decodeCodePointsArray(n)}function decodeCodePointsArray(t){var e=t.length;if(e<=MAX_ARGUMENTS_LENGTH)return String.fromCharCode.apply(String,t);for(var r="",n=0;nn)&&(r=n);for(var f="",i=e;ir)throw new RangeError("Trying to access beyond buffer length")}function checkInt(t,e,r,n,f,i){if(!Buffer.isBuffer(t))throw new TypeError('"buffer" argument must be a Buffer instance');if(e>f||et.length)throw new RangeError("Index out of range")}function objectWriteUInt16(t,e,r,n){e<0&&(e=65535+e+1);for(var f=0,i=Math.min(t.length-r,2);f>>8*(n?f:1-f)}function objectWriteUInt32(t,e,r,n){e<0&&(e=4294967295+e+1);for(var f=0,i=Math.min(t.length-r,4);f>>8*(n?f:3-f)&255}function checkIEEE754(t,e,r,n,f,i){if(r+n>t.length)throw new RangeError("Index out of range");if(r<0)throw new RangeError("Index out of range")}function writeFloat(t,e,r,n,f){return f||checkIEEE754(t,e,r,4,3.4028234663852886e38,-3.4028234663852886e38),ieee754.write(t,e,r,n,23,4),r+4}function writeDouble(t,e,r,n,f){return f||checkIEEE754(t,e,r,8,1.7976931348623157e308,-1.7976931348623157e308),ieee754.write(t,e,r,n,52,8),r+8}function base64clean(t){if(t=stringtrim(t).replace(INVALID_BASE64_RE,""),t.length<2)return"";for(;t.length%4!==0;)t+="=";return t}function stringtrim(t){return t.trim?t.trim():t.replace(/^\s+|\s+$/g,"")}function toHex(t){return t<16?"0"+t.toString(16):t.toString(16)}function utf8ToBytes(t,e){e=e||1/0;for(var r,n=t.length,f=null,i=[],o=0;o55295&&r<57344){if(!f){if(r>56319){(e-=3)>-1&&i.push(239,191,189);continue}if(o+1===n){(e-=3)>-1&&i.push(239,191,189);continue}f=r;continue}if(r<56320){(e-=3)>-1&&i.push(239,191,189),f=r;continue}r=(f-55296<<10|r-56320)+65536}else f&&(e-=3)>-1&&i.push(239,191,189);if(f=null,r<128){if((e-=1)<0)break;i.push(r)}else if(r<2048){if((e-=2)<0)break;i.push(r>>6|192,63&r|128)}else if(r<65536){if((e-=3)<0)break;i.push(r>>12|224,r>>6&63|128,63&r|128)}else{if(!(r<1114112))throw new Error("Invalid code point");if((e-=4)<0)break;i.push(r>>18|240,r>>12&63|128,r>>6&63|128,63&r|128)}}return i}function asciiToBytes(t){for(var e=[],r=0;r>8,f=r%256,i.push(f),i.push(n);return i}function base64ToBytes(t){return base64.toByteArray(base64clean(t))}function blitBuffer(t,e,r,n){for(var f=0;f=e.length||f>=t.length);++f)e[f+r]=t[f];return f}function isnan(t){return t!==t}var base64=require("base64-js"),ieee754=require("ieee754"),isArray=require("isarray");exports.Buffer=Buffer,exports.SlowBuffer=SlowBuffer,exports.INSPECT_MAX_BYTES=50,Buffer.TYPED_ARRAY_SUPPORT=void 0!==global.TYPED_ARRAY_SUPPORT?global.TYPED_ARRAY_SUPPORT:typedArraySupport(),exports.kMaxLength=kMaxLength(),Buffer.poolSize=8192,Buffer._augment=function(t){return t.__proto__=Buffer.prototype,t},Buffer.from=function(t,e,r){return from(null,t,e,r)},Buffer.TYPED_ARRAY_SUPPORT&&(Buffer.prototype.__proto__=Uint8Array.prototype,Buffer.__proto__=Uint8Array,"undefined"!=typeof Symbol&&Symbol.species&&Buffer[Symbol.species]===Buffer&&Object.defineProperty(Buffer,Symbol.species,{value:null,configurable:!0})),Buffer.alloc=function(t,e,r){return alloc(null,t,e,r)},Buffer.allocUnsafe=function(t){return allocUnsafe(null,t)},Buffer.allocUnsafeSlow=function(t){return allocUnsafe(null,t)},Buffer.isBuffer=function(t){return!(null==t||!t._isBuffer)},Buffer.compare=function(t,e){if(!Buffer.isBuffer(t)||!Buffer.isBuffer(e))throw new TypeError("Arguments must be Buffers");if(t===e)return 0;for(var r=t.length,n=e.length,f=0,i=Math.min(r,n);f0&&(t=this.toString("hex",0,e).match(/.{2}/g).join(" "),this.length>e&&(t+=" ... ")),""},Buffer.prototype.compare=function(t,e,r,n,f){if(!Buffer.isBuffer(t))throw new TypeError("Argument must be a Buffer");if(void 0===e&&(e=0),void 0===r&&(r=t?t.length:0),void 0===n&&(n=0),void 0===f&&(f=this.length),e<0||r>t.length||n<0||f>this.length)throw new RangeError("out of range index");if(n>=f&&e>=r)return 0;if(n>=f)return-1;if(e>=r)return 1;if(e>>>=0,r>>>=0,n>>>=0,f>>>=0,this===t)return 0;for(var i=f-n,o=r-e,u=Math.min(i,o),s=this.slice(n,f),a=t.slice(e,r),h=0;hf)&&(r=f),t.length>0&&(r<0||e<0)||e>this.length)throw new RangeError("Attempt to write outside buffer bounds");n||(n="utf8");for(var i=!1;;)switch(n){case"hex":return hexWrite(this,t,e,r);case"utf8":case"utf-8":return utf8Write(this,t,e,r);case"ascii":return asciiWrite(this,t,e,r);case"latin1":case"binary":return latin1Write(this,t,e,r);case"base64":return base64Write(this,t,e,r);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return ucs2Write(this,t,e,r);default:if(i)throw new TypeError("Unknown encoding: "+n);n=(""+n).toLowerCase(),i=!0}},Buffer.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};var MAX_ARGUMENTS_LENGTH=4096;Buffer.prototype.slice=function(t,e){var r=this.length;t=~~t,e=void 0===e?r:~~e,t<0?(t+=r,t<0&&(t=0)):t>r&&(t=r),e<0?(e+=r,e<0&&(e=0)):e>r&&(e=r),e0&&(f*=256);)n+=this[t+--e]*f;return n},Buffer.prototype.readUInt8=function(t,e){return e||checkOffset(t,1,this.length),this[t]},Buffer.prototype.readUInt16LE=function(t,e){return e||checkOffset(t,2,this.length),this[t]|this[t+1]<<8},Buffer.prototype.readUInt16BE=function(t,e){return e||checkOffset(t,2,this.length),this[t]<<8|this[t+1]},Buffer.prototype.readUInt32LE=function(t,e){return e||checkOffset(t,4,this.length),(this[t]|this[t+1]<<8|this[t+2]<<16)+16777216*this[t+3]},Buffer.prototype.readUInt32BE=function(t,e){return e||checkOffset(t,4,this.length),16777216*this[t]+(this[t+1]<<16|this[t+2]<<8|this[t+3])},Buffer.prototype.readIntLE=function(t,e,r){t=0|t,e=0|e,r||checkOffset(t,e,this.length);for(var n=this[t],f=1,i=0;++i=f&&(n-=Math.pow(2,8*e)),n},Buffer.prototype.readIntBE=function(t,e,r){t=0|t,e=0|e,r||checkOffset(t,e,this.length);for(var n=e,f=1,i=this[t+--n];n>0&&(f*=256);)i+=this[t+--n]*f;return f*=128,i>=f&&(i-=Math.pow(2,8*e)),i},Buffer.prototype.readInt8=function(t,e){return e||checkOffset(t,1,this.length),128&this[t]?(255-this[t]+1)*-1:this[t]},Buffer.prototype.readInt16LE=function(t,e){e||checkOffset(t,2,this.length);var r=this[t]|this[t+1]<<8;return 32768&r?4294901760|r:r},Buffer.prototype.readInt16BE=function(t,e){e||checkOffset(t,2,this.length);var r=this[t+1]|this[t]<<8;return 32768&r?4294901760|r:r},Buffer.prototype.readInt32LE=function(t,e){return e||checkOffset(t,4,this.length),this[t]|this[t+1]<<8|this[t+2]<<16|this[t+3]<<24},Buffer.prototype.readInt32BE=function(t,e){return e||checkOffset(t,4,this.length),this[t]<<24|this[t+1]<<16|this[t+2]<<8|this[t+3]},Buffer.prototype.readFloatLE=function(t,e){return e||checkOffset(t,4,this.length),ieee754.read(this,t,!0,23,4)},Buffer.prototype.readFloatBE=function(t,e){return e||checkOffset(t,4,this.length),ieee754.read(this,t,!1,23,4)},Buffer.prototype.readDoubleLE=function(t,e){return e||checkOffset(t,8,this.length),ieee754.read(this,t,!0,52,8)},Buffer.prototype.readDoubleBE=function(t,e){return e||checkOffset(t,8,this.length),ieee754.read(this,t,!1,52,8)},Buffer.prototype.writeUIntLE=function(t,e,r,n){if(t=+t,e=0|e,r=0|r,!n){var f=Math.pow(2,8*r)-1;checkInt(this,t,e,r,f,0)}var i=1,o=0;for(this[e]=255&t;++o=0&&(o*=256);)this[e+i]=t/o&255;return e+r},Buffer.prototype.writeUInt8=function(t,e,r){return t=+t,e=0|e,r||checkInt(this,t,e,1,255,0),Buffer.TYPED_ARRAY_SUPPORT||(t=Math.floor(t)),this[e]=255&t,e+1},Buffer.prototype.writeUInt16LE=function(t,e,r){return t=+t,e=0|e,r||checkInt(this,t,e,2,65535,0),Buffer.TYPED_ARRAY_SUPPORT?(this[e]=255&t,this[e+1]=t>>>8):objectWriteUInt16(this,t,e,!0),e+2},Buffer.prototype.writeUInt16BE=function(t,e,r){return t=+t,e=0|e,r||checkInt(this,t,e,2,65535,0),Buffer.TYPED_ARRAY_SUPPORT?(this[e]=t>>>8,this[e+1]=255&t):objectWriteUInt16(this,t,e,!1),e+2},Buffer.prototype.writeUInt32LE=function(t,e,r){return t=+t,e=0|e,r||checkInt(this,t,e,4,4294967295,0),Buffer.TYPED_ARRAY_SUPPORT?(this[e+3]=t>>>24,this[e+2]=t>>>16,this[e+1]=t>>>8,this[e]=255&t):objectWriteUInt32(this,t,e,!0),e+4},Buffer.prototype.writeUInt32BE=function(t,e,r){return t=+t,e=0|e,r||checkInt(this,t,e,4,4294967295,0),Buffer.TYPED_ARRAY_SUPPORT?(this[e]=t>>>24,this[e+1]=t>>>16,this[e+2]=t>>>8,this[e+3]=255&t):objectWriteUInt32(this,t,e,!1),e+4},Buffer.prototype.writeIntLE=function(t,e,r,n){if(t=+t,e=0|e,!n){var f=Math.pow(2,8*r-1);checkInt(this,t,e,r,f-1,-f)}var i=0,o=1,u=0;for(this[e]=255&t;++i>0)-u&255;return e+r},Buffer.prototype.writeIntBE=function(t,e,r,n){if(t=+t,e=0|e,!n){var f=Math.pow(2,8*r-1);checkInt(this,t,e,r,f-1,-f)}var i=r-1,o=1,u=0;for(this[e+i]=255&t;--i>=0&&(o*=256);)t<0&&0===u&&0!==this[e+i+1]&&(u=1),this[e+i]=(t/o>>0)-u&255;return e+r},Buffer.prototype.writeInt8=function(t,e,r){return t=+t,e=0|e,r||checkInt(this,t,e,1,127,-128),Buffer.TYPED_ARRAY_SUPPORT||(t=Math.floor(t)),t<0&&(t=255+t+1),this[e]=255&t,e+1},Buffer.prototype.writeInt16LE=function(t,e,r){return t=+t,e=0|e,r||checkInt(this,t,e,2,32767,-32768),Buffer.TYPED_ARRAY_SUPPORT?(this[e]=255&t,this[e+1]=t>>>8):objectWriteUInt16(this,t,e,!0),e+2},Buffer.prototype.writeInt16BE=function(t,e,r){return t=+t,e=0|e,r||checkInt(this,t,e,2,32767,-32768),Buffer.TYPED_ARRAY_SUPPORT?(this[e]=t>>>8,this[e+1]=255&t):objectWriteUInt16(this,t,e,!1),e+2},Buffer.prototype.writeInt32LE=function(t,e,r){return t=+t,e=0|e,r||checkInt(this,t,e,4,2147483647,-2147483648),Buffer.TYPED_ARRAY_SUPPORT?(this[e]=255&t,this[e+1]=t>>>8,this[e+2]=t>>>16,this[e+3]=t>>>24):objectWriteUInt32(this,t,e,!0),e+4},Buffer.prototype.writeInt32BE=function(t,e,r){return t=+t,e=0|e,r||checkInt(this,t,e,4,2147483647,-2147483648),t<0&&(t=4294967295+t+1),Buffer.TYPED_ARRAY_SUPPORT?(this[e]=t>>>24,this[e+1]=t>>>16,this[e+2]=t>>>8,this[e+3]=255&t):objectWriteUInt32(this,t,e,!1),e+4},Buffer.prototype.writeFloatLE=function(t,e,r){return writeFloat(this,t,e,!0,r)},Buffer.prototype.writeFloatBE=function(t,e,r){return writeFloat(this,t,e,!1,r)},Buffer.prototype.writeDoubleLE=function(t,e,r){return writeDouble(this,t,e,!0,r)},Buffer.prototype.writeDoubleBE=function(t,e,r){return writeDouble(this,t,e,!1,r)},Buffer.prototype.copy=function(t,e,r,n){if(r||(r=0),n||0===n||(n=this.length),e>=t.length&&(e=t.length),e||(e=0),n>0&&n=this.length)throw new RangeError("sourceStart out of bounds");if(n<0)throw new RangeError("sourceEnd out of bounds");n>this.length&&(n=this.length),t.length-e=0;--f)t[f+e]=this[f+r];else if(i<1e3||!Buffer.TYPED_ARRAY_SUPPORT)for(f=0;f>>=0,r=void 0===r?this.length:r>>>0,t||(t=0);var i;if("number"==typeof t)for(i=e;i0&&this._events[e].length>i&&(this._events[e].warned=!0,console.error("(node) warning: possible EventEmitter memory leak detected. %d listeners added. Use emitter.setMaxListeners() to increase limit.",this._events[e].length),"function"==typeof console.trace&&console.trace())),this},EventEmitter.prototype.on=EventEmitter.prototype.addListener,EventEmitter.prototype.once=function(e,t){function i(){this.removeListener(e,i),n||(n=!0,t.apply(this,arguments))}if(!isFunction(t))throw TypeError("listener must be a function");var n=!1;return i.listener=t,this.on(e,i),this},EventEmitter.prototype.removeListener=function(e,t){var i,n,s,r;if(!isFunction(t))throw TypeError("listener must be a function");if(!this._events||!this._events[e])return this;if(i=this._events[e],s=i.length,n=-1,i===t||isFunction(i.listener)&&i.listener===t)delete this._events[e],this._events.removeListener&&this.emit("removeListener",e,t);else if(isObject(i)){for(r=s;r-- >0;)if(i[r]===t||i[r].listener&&i[r].listener===t){n=r;break}if(n<0)return this;1===i.length?(i.length=0,delete this._events[e]):i.splice(n,1),this._events.removeListener&&this.emit("removeListener",e,t)}return this},EventEmitter.prototype.removeAllListeners=function(e){var t,i;if(!this._events)return this;if(!this._events.removeListener)return 0===arguments.length?this._events={}:this._events[e]&&delete this._events[e],this;if(0===arguments.length){for(t in this._events)"removeListener"!==t&&this.removeAllListeners(t);return this.removeAllListeners("removeListener"),this._events={},this}if(i=this._events[e],isFunction(i))this.removeListener(e,i);else if(i)for(;i.length;)this.removeListener(e,i[i.length-1]);return delete this._events[e],this},EventEmitter.prototype.listeners=function(e){var t;return t=this._events&&this._events[e]?isFunction(this._events[e])?[this._events[e]]:this._events[e].slice():[]},EventEmitter.prototype.listenerCount=function(e){if(this._events){var t=this._events[e];if(isFunction(t))return 1;if(t)return t.length}return 0},EventEmitter.listenerCount=function(e,t){return e.listenerCount(t)}; +},{}],50:[function(require,module,exports){ +exports.read=function(a,o,t,r,h){var M,p,w=8*h-r-1,f=(1<>1,i=-7,N=t?h-1:0,n=t?-1:1,s=a[o+N];for(N+=n,M=s&(1<<-i)-1,s>>=-i,i+=w;i>0;M=256*M+a[o+N],N+=n,i-=8);for(p=M&(1<<-i)-1,M>>=-i,i+=r;i>0;p=256*p+a[o+N],N+=n,i-=8);if(0===M)M=1-e;else{if(M===f)return p?NaN:(s?-1:1)*(1/0);p+=Math.pow(2,r),M-=e}return(s?-1:1)*p*Math.pow(2,M-r)},exports.write=function(a,o,t,r,h,M){var p,w,f,e=8*M-h-1,i=(1<>1,n=23===h?Math.pow(2,-24)-Math.pow(2,-77):0,s=r?0:M-1,u=r?1:-1,l=o<0||0===o&&1/o<0?1:0;for(o=Math.abs(o),isNaN(o)||o===1/0?(w=isNaN(o)?1:0,p=i):(p=Math.floor(Math.log(o)/Math.LN2),o*(f=Math.pow(2,-p))<1&&(p--,f*=2),o+=p+N>=1?n/f:n*Math.pow(2,1-N),o*f>=2&&(p++,f/=2),p+N>=i?(w=0,p=i):p+N>=1?(w=(o*f-1)*Math.pow(2,h),p+=N):(w=o*Math.pow(2,N-1)*Math.pow(2,h),p=0));h>=8;a[t+s]=255&w,s+=u,w/=256,h-=8);for(p=p<0;a[t+s]=255&p,s+=u,p/=256,e-=8);a[t+s-u]|=128*l}; + +},{}],51:[function(require,module,exports){ +"function"==typeof Object.create?module.exports=function(t,e){t.super_=e,t.prototype=Object.create(e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}})}:module.exports=function(t,e){t.super_=e;var o=function(){};o.prototype=e.prototype,t.prototype=new o,t.prototype.constructor=t}; +},{}],52:[function(require,module,exports){ +function isBuffer(f){return!!f.constructor&&"function"==typeof f.constructor.isBuffer&&f.constructor.isBuffer(f)}function isSlowBuffer(f){return"function"==typeof f.readFloatLE&&"function"==typeof f.slice&&isBuffer(f.slice(0,0))}module.exports=function(f){return null!=f&&(isBuffer(f)||isSlowBuffer(f)||!!f._isBuffer)}; +},{}],53:[function(require,module,exports){ +var toString={}.toString;module.exports=Array.isArray||function(r){return"[object Array]"==toString.call(r)}; + +},{}],54:[function(require,module,exports){ +(function (process){ +"use strict";function nextTick(e,n,c,r){if("function"!=typeof e)throw new TypeError('"callback" argument must be a function');var s,t,o=arguments.length;switch(o){case 0:case 1:return process.nextTick(e);case 2:return process.nextTick(function(){e.call(null,n)});case 3:return process.nextTick(function(){e.call(null,n,c)});case 4:return process.nextTick(function(){e.call(null,n,c,r)});default:for(s=new Array(o-1),t=0;t1)for(var u=1;u0)if(t.ended&&!a){var d=new Error("stream.push() after EOF");e.emit("error",d)}else if(t.endEmitted&&a){var o=new Error("stream.unshift() after end event");e.emit("error",o)}else{var u;!t.decoder||a||n||(r=t.decoder.write(r),u=!t.objectMode&&0===r.length),a||(t.reading=!1),u||(t.flowing&&0===t.length&&!t.sync?(e.emit("data",r),e.read(0)):(t.length+=t.objectMode?1:r.length,a?t.buffer.unshift(r):t.buffer.push(r),t.needReadable&&emitReadable(e))),maybeReadMore(e,t)}else a||(t.reading=!1);return needMoreData(t)}function needMoreData(e){return!e.ended&&(e.needReadable||e.length=MAX_HWM?e=MAX_HWM:(e--,e|=e>>>1,e|=e>>>2,e|=e>>>4,e|=e>>>8,e|=e>>>16,e++),e}function howMuchToRead(e,t){return e<=0||0===t.length&&t.ended?0:t.objectMode?1:e!==e?t.flowing&&t.length?t.buffer.head.data.length:t.length:(e>t.highWaterMark&&(t.highWaterMark=computeNewHighWaterMark(e)),e<=t.length?e:t.ended?t.length:(t.needReadable=!0,0))}function chunkInvalid(e,t){var r=null;return Buffer.isBuffer(t)||"string"==typeof t||null===t||void 0===t||e.objectMode||(r=new TypeError("Invalid non-string/buffer chunk")),r}function onEofChunk(e,t){if(!t.ended){if(t.decoder){var r=t.decoder.end();r&&r.length&&(t.buffer.push(r),t.length+=t.objectMode?1:r.length)}t.ended=!0,emitReadable(e)}}function emitReadable(e){var t=e._readableState;t.needReadable=!1,t.emittedReadable||(debug("emitReadable",t.flowing),t.emittedReadable=!0,t.sync?processNextTick(emitReadable_,e):emitReadable_(e))}function emitReadable_(e){debug("emit readable"),e.emit("readable"),flow(e)}function maybeReadMore(e,t){t.readingMore||(t.readingMore=!0,processNextTick(maybeReadMore_,e,t))}function maybeReadMore_(e,t){for(var r=t.length;!t.reading&&!t.flowing&&!t.ended&&t.length=t.length?(r=t.decoder?t.buffer.join(""):1===t.buffer.length?t.buffer.head.data:t.buffer.concat(t.length),t.buffer.clear()):r=fromListPartial(e,t.buffer,t.decoder),r}function fromListPartial(e,t,r){var n;return ei.length?i.length:e;if(a+=d===i.length?i:i.slice(0,e),e-=d,0===e){d===i.length?(++n,r.next?t.head=r.next:t.head=t.tail=null):(t.head=r,r.data=i.slice(d));break}++n}return t.length-=n,a}function copyFromBuffer(e,t){var r=bufferShim.allocUnsafe(e),n=t.head,a=1;for(n.data.copy(r),e-=n.data.length;n=n.next;){var i=n.data,d=e>i.length?i.length:e;if(i.copy(r,r.length-e,0,d),e-=d,0===e){d===i.length?(++a,n.next?t.head=n.next:t.head=t.tail=null):(t.head=n,n.data=i.slice(d));break}++a}return t.length-=a,r}function endReadable(e){var t=e._readableState;if(t.length>0)throw new Error('"endReadable()" called on non-empty stream');t.endEmitted||(t.ended=!0,processNextTick(endReadableNT,t,e))}function endReadableNT(e,t){e.endEmitted||0!==e.length||(e.endEmitted=!0,t.readable=!1,t.emit("end"))}function forEach(e,t){for(var r=0,n=e.length;r=t.highWaterMark||t.ended))return debug("read: emitReadable",t.length,t.ended),0===t.length&&t.ended?endReadable(this):emitReadable(this),null;if(e=howMuchToRead(e,t),0===e&&t.ended)return 0===t.length&&endReadable(this),null;var n=t.needReadable;debug("need readable",n),(0===t.length||t.length-e0?fromList(e,t):null,null===a?(t.needReadable=!0,e=0):t.length-=e,0===t.length&&(t.ended||(t.needReadable=!0),r!==e&&t.ended&&endReadable(this)),null!==a&&this.emit("data",a),a},Readable.prototype._read=function(e){this.emit("error",new Error("not implemented"))},Readable.prototype.pipe=function(e,t){function r(e){debug("onunpipe"),e===s&&a()}function n(){debug("onend"),e.end()}function a(){debug("cleanup"),e.removeListener("close",o),e.removeListener("finish",u),e.removeListener("drain",c),e.removeListener("error",d),e.removeListener("unpipe",r),s.removeListener("end",n),s.removeListener("end",a),s.removeListener("data",i),g=!0,!h.awaitDrain||e._writableState&&!e._writableState.needDrain||c()}function i(t){debug("ondata"),b=!1;var r=e.write(t);!1!==r||b||((1===h.pipesCount&&h.pipes===e||h.pipesCount>1&&indexOf(h.pipes,e)!==-1)&&!g&&(debug("false write response, pause",s._readableState.awaitDrain),s._readableState.awaitDrain++,b=!0),s.pause())}function d(t){debug("onerror",t),l(),e.removeListener("error",d),0===EElistenerCount(e,"error")&&e.emit("error",t)}function o(){e.removeListener("finish",u),l()}function u(){debug("onfinish"),e.removeListener("close",o),l()}function l(){debug("unpipe"),s.unpipe(e)}var s=this,h=this._readableState;switch(h.pipesCount){case 0:h.pipes=e;break;case 1:h.pipes=[h.pipes,e];break;default:h.pipes.push(e)}h.pipesCount+=1,debug("pipe count=%d opts=%j",h.pipesCount,t);var f=(!t||t.end!==!1)&&e!==process.stdout&&e!==process.stderr,p=f?n:a;h.endEmitted?processNextTick(p):s.once("end",p),e.on("unpipe",r);var c=pipeOnDrain(s);e.on("drain",c);var g=!1,b=!1;return s.on("data",i),prependListener(e,"error",d),e.once("close",o),e.once("finish",u),e.emit("pipe",s),h.flowing||(debug("pipe resume"),s.resume()),e},Readable.prototype.unpipe=function(e){var t=this._readableState;if(0===t.pipesCount)return this;if(1===t.pipesCount)return e&&e!==t.pipes?this:(e||(e=t.pipes),t.pipes=null,t.pipesCount=0,t.flowing=!1,e&&e.emit("unpipe",this),this);if(!e){var r=t.pipes,n=t.pipesCount;t.pipes=null,t.pipesCount=0,t.flowing=!1;for(var a=0;a-1?setImmediate:processNextTick;Writable.WritableState=WritableState;var util=require("core-util-is");util.inherits=require("inherits");var internalUtil={deprecate:require("util-deprecate")},Stream;!function(){try{Stream=require("stream")}catch(e){}finally{Stream||(Stream=require("events").EventEmitter)}}();var Buffer=require("buffer").Buffer,bufferShim=require("buffer-shims");util.inherits(Writable,Stream);var Duplex;WritableState.prototype.getBuffer=function(){for(var e=this.bufferedRequest,t=[];e;)t.push(e),e=e.next;return t},function(){try{Object.defineProperty(WritableState.prototype,"buffer",{get:internalUtil.deprecate(function(){return this.getBuffer()},"_writableState.buffer is deprecated. Use _writableState.getBuffer instead.")})}catch(e){}}();var Duplex;Writable.prototype.pipe=function(){this.emit("error",new Error("Cannot pipe, not readable"))},Writable.prototype.write=function(e,t,r){var i=this._writableState,n=!1;return"function"==typeof t&&(r=t,t=null),Buffer.isBuffer(e)?t="buffer":t||(t=i.defaultEncoding),"function"!=typeof r&&(r=nop),i.ended?writeAfterEnd(this,r):validChunk(this,i,e,r)&&(i.pendingcb++,n=writeOrBuffer(this,i,e,t,r)),n},Writable.prototype.cork=function(){var e=this._writableState;e.corked++},Writable.prototype.uncork=function(){var e=this._writableState;e.corked&&(e.corked--,e.writing||e.corked||e.finished||e.bufferProcessing||!e.bufferedRequest||clearBuffer(this,e))},Writable.prototype.setDefaultEncoding=function(e){if("string"==typeof e&&(e=e.toLowerCase()),!(["hex","utf8","utf-8","ascii","binary","base64","ucs2","ucs-2","utf16le","utf-16le","raw"].indexOf((e+"").toLowerCase())>-1))throw new TypeError("Unknown encoding: "+e);return this._writableState.defaultEncoding=e,this},Writable.prototype._write=function(e,t,r){r(new Error("not implemented"))},Writable.prototype._writev=null,Writable.prototype.end=function(e,t,r){var i=this._writableState;"function"==typeof e?(r=e,e=null,t=null):"function"==typeof t&&(r=t,t=null),null!==e&&void 0!==e&&this.write(e,t),i.corked&&(i.corked=1,this.uncork()),i.ending||i.finished||endWritable(this,i,r)}; +}).call(this,require('_process')) +},{"./_stream_duplex":57,"_process":55,"buffer":47,"buffer-shims":46,"core-util-is":48,"events":49,"inherits":51,"process-nextick-args":54,"stream":67,"util-deprecate":69}],62:[function(require,module,exports){ +"use strict";function BufferList(){this.head=null,this.tail=null,this.length=0}var Buffer=require("buffer").Buffer,bufferShim=require("buffer-shims");module.exports=BufferList,BufferList.prototype.push=function(t){var e={data:t,next:null};this.length>0?this.tail.next=e:this.head=e,this.tail=e,++this.length},BufferList.prototype.unshift=function(t){var e={data:t,next:this.head};0===this.length&&(this.tail=e),this.head=e,++this.length},BufferList.prototype.shift=function(){if(0!==this.length){var t=this.head.data;return 1===this.length?this.head=this.tail=null:this.head=this.head.next,--this.length,t}},BufferList.prototype.clear=function(){this.head=this.tail=null,this.length=0},BufferList.prototype.join=function(t){if(0===this.length)return"";for(var e=this.head,i=""+e.data;e=e.next;)i+=t+e.data;return i},BufferList.prototype.concat=function(t){if(0===this.length)return bufferShim.alloc(0);if(1===this.length)return this.head.data;for(var e=bufferShim.allocUnsafe(t>>>0),i=this.head,h=0;i;)i.data.copy(e,h),h+=i.data.length,i=i.next;return e}; + +},{"buffer":47,"buffer-shims":46}],63:[function(require,module,exports){ +module.exports=require("./lib/_stream_passthrough.js"); + +},{"./lib/_stream_passthrough.js":58}],64:[function(require,module,exports){ +(function (process){ +var Stream=function(){try{return require("stream")}catch(r){}}();exports=module.exports=require("./lib/_stream_readable.js"),exports.Stream=Stream||exports,exports.Readable=exports,exports.Writable=require("./lib/_stream_writable.js"),exports.Duplex=require("./lib/_stream_duplex.js"),exports.Transform=require("./lib/_stream_transform.js"),exports.PassThrough=require("./lib/_stream_passthrough.js"),!process.browser&&"disable"===process.env.READABLE_STREAM&&Stream&&(module.exports=Stream); + +}).call(this,require('_process')) +},{"./lib/_stream_duplex.js":57,"./lib/_stream_passthrough.js":58,"./lib/_stream_readable.js":59,"./lib/_stream_transform.js":60,"./lib/_stream_writable.js":61,"_process":55,"stream":67}],65:[function(require,module,exports){ +module.exports=require("./lib/_stream_transform.js"); + +},{"./lib/_stream_transform.js":60}],66:[function(require,module,exports){ +module.exports=require("./lib/_stream_writable.js"); + +},{"./lib/_stream_writable.js":61}],67:[function(require,module,exports){ +function Stream(){EE.call(this)}module.exports=Stream;var EE=require("events").EventEmitter,inherits=require("inherits");inherits(Stream,EE),Stream.Readable=require("readable-stream/readable.js"),Stream.Writable=require("readable-stream/writable.js"),Stream.Duplex=require("readable-stream/duplex.js"),Stream.Transform=require("readable-stream/transform.js"),Stream.PassThrough=require("readable-stream/passthrough.js"),Stream.Stream=Stream,Stream.prototype.pipe=function(e,r){function t(r){e.writable&&!1===e.write(r)&&m.pause&&m.pause()}function n(){m.readable&&m.resume&&m.resume()}function a(){u||(u=!0,e.end())}function o(){u||(u=!0,"function"==typeof e.destroy&&e.destroy())}function i(e){if(s(),0===EE.listenerCount(this,"error"))throw e}function s(){m.removeListener("data",t),e.removeListener("drain",n),m.removeListener("end",a),m.removeListener("close",o),m.removeListener("error",i),e.removeListener("error",i),m.removeListener("end",s),m.removeListener("close",s),e.removeListener("close",s)}var m=this;m.on("data",t),e.on("drain",n),e._isStdio||r&&r.end===!1||(m.on("end",a),m.on("close",o));var u=!1;return m.on("error",i),e.on("error",i),m.on("end",s),m.on("close",s),e.on("close",s),e.emit("pipe",m),e}; +},{"events":49,"inherits":51,"readable-stream/duplex.js":56,"readable-stream/passthrough.js":63,"readable-stream/readable.js":64,"readable-stream/transform.js":65,"readable-stream/writable.js":66}],68:[function(require,module,exports){ +function assertEncoding(e){if(e&&!isBufferEncoding(e))throw new Error("Unknown encoding: "+e)}function passThroughWrite(e){return e.toString(this.encoding)}function utf16DetectIncompleteChar(e){this.charReceived=e.length%2,this.charLength=this.charReceived?2:0}function base64DetectIncompleteChar(e){this.charReceived=e.length%3,this.charLength=this.charReceived?3:0}var Buffer=require("buffer").Buffer,isBufferEncoding=Buffer.isEncoding||function(e){switch(e&&e.toLowerCase()){case"hex":case"utf8":case"utf-8":case"ascii":case"binary":case"base64":case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":case"raw":return!0;default:return!1}},StringDecoder=exports.StringDecoder=function(e){switch(this.encoding=(e||"utf8").toLowerCase().replace(/[-_]/,""),assertEncoding(e),this.encoding){case"utf8":this.surrogateSize=3;break;case"ucs2":case"utf16le":this.surrogateSize=2,this.detectIncompleteChar=utf16DetectIncompleteChar;break;case"base64":this.surrogateSize=3,this.detectIncompleteChar=base64DetectIncompleteChar;break;default:return void(this.write=passThroughWrite)}this.charBuffer=new Buffer(6),this.charReceived=0,this.charLength=0};StringDecoder.prototype.write=function(e){for(var t="";this.charLength;){var r=e.length>=this.charLength-this.charReceived?this.charLength-this.charReceived:e.length;if(e.copy(this.charBuffer,this.charReceived,0,r),this.charReceived+=r,this.charReceived=55296&&h<=56319)){if(this.charReceived=this.charLength=0,0===e.length)return t;break}this.charLength+=this.surrogateSize,t=""}this.detectIncompleteChar(e);var i=e.length;this.charLength&&(e.copy(this.charBuffer,0,e.length-this.charReceived,i),i-=this.charReceived),t+=e.toString(this.encoding,0,i);var i=t.length-1,h=t.charCodeAt(i);if(h>=55296&&h<=56319){var c=this.surrogateSize;return this.charLength+=c,this.charReceived+=c,this.charBuffer.copy(this.charBuffer,c,0,c),e.copy(this.charBuffer,0,0,c),t.substring(0,i)}return t},StringDecoder.prototype.detectIncompleteChar=function(e){for(var t=e.length>=3?3:e.length;t>0;t--){var r=e[e.length-t];if(1==t&&r>>5==6){this.charLength=2;break}if(t<=2&&r>>4==14){this.charLength=3;break}if(t<=3&&r>>3==30){this.charLength=4;break}}this.charReceived=t},StringDecoder.prototype.end=function(e){var t="";if(e&&e.length&&(t=this.write(e)),this.charReceived){var r=this.charReceived,h=this.charBuffer,i=this.encoding;t+=h.slice(0,r).toString(i)}return t}; +},{"buffer":47}],69:[function(require,module,exports){ +(function (global){ +function deprecate(r,e){function o(){if(!t){if(config("throwDeprecation"))throw new Error(e);config("traceDeprecation")?console.trace(e):console.warn(e),t=!0}return r.apply(this,arguments)}if(config("noDeprecation"))return r;var t=!1;return o}function config(r){try{if(!global.localStorage)return!1}catch(r){return!1}var e=global.localStorage[r];return null!=e&&"true"===String(e).toLowerCase()}module.exports=deprecate; + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}]},{},[43]); diff --git a/dist/brain.js b/dist/brain.js deleted file mode 100644 index a40d44cdc..000000000 --- a/dist/brain.js +++ /dev/null @@ -1,36 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.TrainStream = exports.NeuralNetwork = exports.lookup = exports.likely = exports.crossValidate = undefined; - -var _crossValidate = require('./cross-validate'); - -var _crossValidate2 = _interopRequireDefault(_crossValidate); - -var _likely = require('./likely'); - -var _likely2 = _interopRequireDefault(_likely); - -var _lookup = require('./lookup'); - -var _lookup2 = _interopRequireDefault(_lookup); - -var _neuralNetwork = require('./neural-network'); - -var _neuralNetwork2 = _interopRequireDefault(_neuralNetwork); - -var _trainStream = require('./train-stream'); - -var _trainStream2 = _interopRequireDefault(_trainStream); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -exports.default = _neuralNetwork2.default; -exports.crossValidate = _crossValidate2.default; -exports.likely = _likely2.default; -exports.lookup = _lookup2.default; -exports.NeuralNetwork = _neuralNetwork2.default; -exports.TrainStream = _trainStream2.default; -//# sourceMappingURL=brain.js.map diff --git a/dist/brain.js.map b/dist/brain.js.map deleted file mode 100644 index ef93ecbd7..000000000 --- a/dist/brain.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../src/brain.js"],"names":["crossValidate","likely","lookup","NeuralNetwork","TrainStream"],"mappings":";;;;;;;AAAA;;;;AACA;;;;AACA;;;;AACA;;;;AACA;;;;;;;QAKEA,a;QACAC,M;QACAC,M;QACAC,a;QACAC,W","file":"brain.js","sourcesContent":["import crossValidate from './cross-validate';\nimport likely from './likely';\nimport lookup from './lookup';\nimport NeuralNetwork from './neural-network';\nimport TrainStream from './train-stream';\n\nexport default NeuralNetwork;\n\nexport {\n crossValidate,\n likely,\n lookup,\n NeuralNetwork,\n TrainStream\n};\n"]} \ No newline at end of file diff --git a/dist/cross-validate.js b/dist/cross-validate.js index d2bb590db..3cd7f31a7 100644 --- a/dist/cross-validate.js +++ b/dist/cross-validate.js @@ -160,4 +160,4 @@ function crossValidate(Classifier, data, opts, trainOpts, k) { misclasses: misclasses }; } -//# sourceMappingURL=cross-validate.js.map +//# sourceMappingURL=cross-validate.js.map \ No newline at end of file diff --git a/dist/index.js b/dist/index.js index 8d2730530..5a8744d44 100644 --- a/dist/index.js +++ b/dist/index.js @@ -24,6 +24,18 @@ var _trainStream = require('./train-stream'); var _trainStream2 = _interopRequireDefault(_trainStream); +var _rnn = require('./recurrent/rnn'); + +var _rnn2 = _interopRequireDefault(_rnn); + +var _lstm = require('./recurrent/lstm'); + +var _lstm2 = _interopRequireDefault(_lstm); + +var _gru = require('./recurrent/gru'); + +var _gru2 = _interopRequireDefault(_gru); + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } exports.default = { @@ -31,6 +43,11 @@ exports.default = { likely: _likely2.default, lookup: _lookup2.default, NeuralNetwork: _neuralNetwork2.default, - TrainStream: _trainStream2.default + TrainStream: _trainStream2.default, + recurrent: { + RNN: _rnn2.default, + LSTM: _lstm2.default, + GRU: _gru2.default + } }; -//# sourceMappingURL=index.js.map +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/index.js.map b/dist/index.js.map index 92158976c..f784f3ac5 100644 --- a/dist/index.js.map +++ b/dist/index.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/index.js"],"names":["crossValidate","likely","lookup","NeuralNetwork","TrainStream"],"mappings":";;;;;;AAAA;;;;AACA;;;;AACA;;;;AACA;;;;AACA;;;;;;kBAEe;AACbA,wCADa;AAEbC,0BAFa;AAGbC,0BAHa;AAIbC,wCAJa;AAKbC;AALa,C","file":"index.js","sourcesContent":["import crossValidate from './cross-validate';\nimport likely from './likely';\nimport lookup from './lookup';\nimport NeuralNetwork from './neural-network';\nimport TrainStream from './train-stream';\n\nexport default {\n crossValidate,\n likely,\n lookup,\n NeuralNetwork,\n TrainStream\n};\n"]} \ No newline at end of file +{"version":3,"sources":["../src/index.js"],"names":["crossValidate","likely","lookup","NeuralNetwork","TrainStream","recurrent","RNN","LSTM","GRU"],"mappings":";;;;;;AAAA;;;;AACA;;;;AACA;;;;AACA;;;;AACA;;;;AACA;;;;AACA;;;;AACA;;;;;;kBAEe;AACbA,wCADa;AAEbC,0BAFa;AAGbC,0BAHa;AAIbC,wCAJa;AAKbC,oCALa;AAMbC,aAAW;AACTC,sBADS;AAETC,wBAFS;AAGTC;AAHS;AANE,C","file":"index.js","sourcesContent":["import crossValidate from './cross-validate';\nimport likely from './likely';\nimport lookup from './lookup';\nimport NeuralNetwork from './neural-network';\nimport TrainStream from './train-stream';\nimport RNN from './recurrent/rnn';\nimport LSTM from './recurrent/lstm';\nimport GRU from './recurrent/gru';\n\nexport default {\n crossValidate,\n likely,\n lookup,\n NeuralNetwork,\n TrainStream,\n recurrent: {\n RNN,\n LSTM,\n GRU\n }\n};\n"]} \ No newline at end of file diff --git a/dist/likely.js b/dist/likely.js index c01c12026..de9c4759b 100644 --- a/dist/likely.js +++ b/dist/likely.js @@ -25,4 +25,4 @@ function likely(input, net) { } return maxProp; } -//# sourceMappingURL=likely.js.map +//# sourceMappingURL=likely.js.map \ No newline at end of file diff --git a/dist/lookup.js b/dist/lookup.js index 293083e5b..94e714804 100644 --- a/dist/lookup.js +++ b/dist/lookup.js @@ -104,4 +104,4 @@ var lookup = function () { }(); exports.default = lookup; -//# sourceMappingURL=lookup.js.map +//# sourceMappingURL=lookup.js.map \ No newline at end of file diff --git a/dist/neural-network.js b/dist/neural-network.js index 59284de67..7848e4b7d 100644 --- a/dist/neural-network.js +++ b/dist/neural-network.js @@ -177,10 +177,11 @@ var NeuralNetwork = function () { }, { key: 'train', - value: function train(data, options) { + value: function train(data) { + var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; + data = this.formatData(data); - options = options || {}; var iterations = options.iterations || 20000; var errorThresh = options.errorThresh || 0.005; var log = options.log ? typeof options.log === 'function' ? options.log : console.log : false; @@ -597,4 +598,4 @@ var NeuralNetwork = function () { }(); exports.default = NeuralNetwork; -//# sourceMappingURL=neural-network.js.map +//# sourceMappingURL=neural-network.js.map \ No newline at end of file diff --git a/dist/neural-network.js.map b/dist/neural-network.js.map index f030cada7..3c88b7ff0 100644 --- a/dist/neural-network.js.map +++ b/dist/neural-network.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/neural-network.js"],"names":["NeuralNetwork","options","learningRate","momentum","hiddenSizes","hiddenLayers","binaryThresh","sizes","outputLayer","biases","weights","outputs","deltas","changes","errors","keepNetworkIntact","length","layer","size","Array","node","prevSize","input","inputLookup","toArray","output","runInput","outputLookup","toHash","sum","k","Math","exp","data","formatData","iterations","errorThresh","log","console","logPeriod","callback","callbackPeriod","inputSize","outputSize","push","max","floor","forEach","unshift","initialize","error","i","j","err","trainPattern","target","calculateDeltas","adjustWeights","incoming","delta","change","constructor","tmp","datum","Float64Array","buildLookup","map","value","array","Object","assign","isBinary","falsePos","falseNeg","truePos","trueNeg","misclasses","actual","expected","indexOf","misclass","stats","total","precision","recall","accuracy","layers","nodes","keys","bias","index","json","lookupFromHash","toJSON","jsonString","JSON","stringify","Function","opts","neuralNetwork","trainStream"],"mappings":"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","file":"neural-network.js","sourcesContent":["import lookup from './lookup';\nimport TrainStream from './train-stream';\nimport max from './utilities/max';\nimport mse from './utilities/mse';\nimport randos from './utilities/randos';\nimport range from './utilities/range';\nimport toArray from './utilities/to-array';\nimport zeros from './utilities/zeros';\n\n/**\n *\n * @param {object} options\n * @constructor\n */\nexport default class NeuralNetwork {\n constructor(options) {\n options = options || {};\n this.learningRate = options.learningRate || 0.3;\n this.momentum = options.momentum || 0.1;\n this.hiddenSizes = options.hiddenLayers;\n\n this.binaryThresh = options.binaryThresh || 0.5;\n\n this.sizes = null;\n this.outputLayer = null;\n this.biases = null; // weights for bias nodes\n this.weights = null;\n this.outputs = null;\n\n // state for training\n this.deltas = null;\n this.changes = null; // for momentum\n this.errors = null;\n }\n\n /**\n *\n * @param {} sizes\n * @param {Boolean} keepNetworkIntact\n */\n initialize(sizes, keepNetworkIntact) {\n this.sizes = sizes;\n this.outputLayer = this.sizes.length - 1;\n\n if (!keepNetworkIntact) {\n this.biases = []; // weights for bias nodes\n this.weights = [];\n this.outputs = [];\n }\n\n // state for training\n this.deltas = [];\n this.changes = []; // for momentum\n this.errors = [];\n\n for (let layer = 0; layer <= this.outputLayer; layer++) {\n let size = this.sizes[layer];\n this.deltas[layer] = zeros(size);\n this.errors[layer] = zeros(size);\n if (!keepNetworkIntact) {\n this.outputs[layer] = zeros(size);\n }\n\n if (layer > 0) {\n this.biases[layer] = randos(size);\n if (!keepNetworkIntact) {\n this.weights[layer] = new Array(size);\n }\n this.changes[layer] = new Array(size);\n\n for (let node = 0; node < size; node++) {\n let prevSize = this.sizes[layer - 1];\n if (!keepNetworkIntact) {\n this.weights[layer][node] = randos(prevSize);\n }\n this.changes[layer][node] = zeros(prevSize);\n }\n }\n }\n }\n\n /**\n *\n * @param input\n * @returns {*}\n */\n run(input) {\n if (this.inputLookup) {\n input = lookup.toArray(this.inputLookup, input);\n }\n\n let output = this.runInput(input);\n\n if (this.outputLookup) {\n output = lookup.toHash(this.outputLookup, output);\n }\n return output;\n }\n\n /**\n *\n * @param input\n * @returns {*}\n */\n runInput(input) {\n this.outputs[0] = input; // set output state of input layer\n\n let output = null;\n for (let layer = 1; layer <= this.outputLayer; layer++) {\n for (let node = 0; node < this.sizes[layer]; node++) {\n let weights = this.weights[layer][node];\n\n let sum = this.biases[layer][node];\n for (let k = 0; k < weights.length; k++) {\n sum += weights[k] * input[k];\n }\n this.outputs[layer][node] = 1 / (1 + Math.exp(-sum));\n }\n output = input = this.outputs[layer];\n }\n return output;\n }\n\n /**\n *\n * @param data\n * @param options\n * @returns {{error: number, iterations: number}}\n */\n train(data, options) {\n data = this.formatData(data);\n\n options = options || {};\n let iterations = options.iterations || 20000;\n let errorThresh = options.errorThresh || 0.005;\n let log = options.log ? (typeof options.log === 'function' ? options.log : console.log) : false;\n let logPeriod = options.logPeriod || 10;\n let learningRate = options.learningRate || this.learningRate || 0.3;\n let callback = options.callback;\n let callbackPeriod = options.callbackPeriod || 10;\n let sizes = [];\n let inputSize = data[0].input.length;\n let outputSize = data[0].output.length;\n let hiddenSizes = this.hiddenSizes;\n if (!hiddenSizes) {\n sizes.push(Math.max(3, Math.floor(inputSize / 2)));\n } else {\n hiddenSizes.forEach(size => {\n sizes.push(size);\n });\n }\n\n sizes.unshift(inputSize);\n sizes.push(outputSize);\n\n this.initialize(sizes, options.keepNetworkIntact);\n\n let error = 1;\n let i;\n for (i = 0; i < iterations && error > errorThresh; i++) {\n let sum = 0;\n for (let j = 0; j < data.length; j++) {\n let err = this.trainPattern(data[j].input, data[j].output, learningRate);\n sum += err;\n }\n error = sum / data.length;\n\n if (log && (i % logPeriod == 0)) {\n log('iterations:', i, 'training error:', error);\n }\n if (callback && (i % callbackPeriod == 0)) {\n callback({ error: error, iterations: i });\n }\n }\n\n return {\n error: error,\n iterations: i\n };\n }\n\n /**\n *\n * @param input\n * @param target\n * @param learningRate\n */\n trainPattern(input, target, learningRate) {\n learningRate = learningRate || this.learningRate;\n\n // forward propagate\n this.runInput(input);\n\n // back propagate\n this.calculateDeltas(target);\n this.adjustWeights(learningRate);\n\n let error = mse(this.errors[this.outputLayer]);\n return error;\n }\n\n /**\n *\n * @param target\n */\n calculateDeltas(target) {\n for (let layer = this.outputLayer; layer >= 0; layer--) {\n for (let node = 0; node < this.sizes[layer]; node++) {\n let output = this.outputs[layer][node];\n\n let error = 0;\n if (layer == this.outputLayer) {\n error = target[node] - output;\n }\n else {\n let deltas = this.deltas[layer + 1];\n for (let k = 0; k < deltas.length; k++) {\n error += deltas[k] * this.weights[layer + 1][k][node];\n }\n }\n this.errors[layer][node] = error;\n this.deltas[layer][node] = error * output * (1 - output);\n }\n }\n }\n\n /**\n *\n * @param learningRate\n */\n adjustWeights(learningRate) {\n for (let layer = 1; layer <= this.outputLayer; layer++) {\n let incoming = this.outputs[layer - 1];\n\n for (let node = 0; node < this.sizes[layer]; node++) {\n let delta = this.deltas[layer][node];\n\n for (let k = 0; k < incoming.length; k++) {\n let change = this.changes[layer][node][k];\n\n change = (learningRate * delta * incoming[k])\n + (this.momentum * change);\n\n this.changes[layer][node][k] = change;\n this.weights[layer][node][k] += change;\n }\n this.biases[layer][node] += learningRate * delta;\n }\n }\n }\n\n /**\n *\n * @param data\n * @returns {*}\n */\n formatData(data) {\n if (data.constructor !== Array) { // turn stream datum into array\n let tmp = [];\n tmp.push(data);\n data = tmp;\n }\n // turn sparse hash input into arrays with 0s as filler\n let datum = data[0].input;\n if (datum.constructor !== Array && !(datum instanceof Float64Array)) {\n if (!this.inputLookup) {\n this.inputLookup = lookup.buildLookup(data.map(value => value['input']));\n }\n data = data.map(datum => {\n let array = lookup.toArray(this.inputLookup, datum.input);\n return Object.assign({}, datum, { input: array });\n }, this);\n }\n\n if (data[0].output.constructor !== Array) {\n if (!this.outputLookup) {\n this.outputLookup = lookup.buildLookup(data.map(value => value['output']));\n }\n data = data.map(datum => {\n let array = lookup.toArray(this.outputLookup, datum.output);\n return Object.assign({}, datum, { output: array });\n }, this);\n }\n return data;\n }\n\n /**\n *\n * @param data\n * @returns {\n * {\n * error: number,\n * misclasses: Array\n * }\n * }\n */\n test(data) {\n data = this.formatData(data);\n\n // for binary classification problems with one output node\n let isBinary = data[0].output.length == 1;\n let falsePos = 0;\n let falseNeg = 0;\n let truePos = 0;\n let trueNeg = 0;\n\n // for classification problems\n let misclasses = [];\n\n // run each pattern through the trained network and collect\n // error and misclassification statistics\n let sum = 0;\n for (let i = 0; i < data.length; i++) {\n let output = this.runInput(data[i].input);\n let target = data[i].output;\n\n let actual, expected;\n if (isBinary) {\n actual = output[0] > this.binaryThresh ? 1 : 0;\n expected = target[0];\n }\n else {\n actual = output.indexOf(max(output));\n expected = target.indexOf(max(target));\n }\n\n if (actual != expected) {\n let misclass = data[i];\n Object.assign(misclass, {\n actual: actual,\n expected: expected\n });\n misclasses.push(misclass);\n }\n\n if (isBinary) {\n if (actual == 0 && expected == 0) {\n trueNeg++;\n }\n else if (actual == 1 && expected == 1) {\n truePos++;\n }\n else if (actual == 0 && expected == 1) {\n falseNeg++;\n }\n else if (actual == 1 && expected == 0) {\n falsePos++;\n }\n }\n\n let errors = output.map((value, i) => {\n return target[i] - value;\n });\n sum += mse(errors);\n }\n let error = sum / data.length;\n\n let stats = {\n error: error,\n misclasses: misclasses\n };\n\n if (isBinary) {\n Object.assign(stats, {\n trueNeg: trueNeg,\n truePos: truePos,\n falseNeg: falseNeg,\n falsePos: falsePos,\n total: data.length,\n precision: truePos / (truePos + falsePos),\n recall: truePos / (truePos + falseNeg),\n accuracy: (trueNeg + truePos) / data.length\n });\n }\n return stats;\n }\n\n /**\n *\n * @returns\n * {\n * layers: [\n * {\n * x: {},\n * y: {}\n * },\n * {\n * '0': {\n * bias: -0.98771313,\n * weights: {\n * x: 0.8374838,\n * y: 1.245858\n * },\n * '1': {\n * bias: 3.48192004,\n * weights: {\n * x: 1.7825821,\n * y: -2.67899\n * }\n * }\n * },\n * {\n * f: {\n * bias: 0.27205739,\n * weights: {\n * '0': 1.3161821,\n * '1': 2.00436\n * }\n * }\n * }\n * ]\n * }\n */\n toJSON() {\n let layers = [];\n for (let layer = 0; layer <= this.outputLayer; layer++) {\n layers[layer] = {};\n\n let nodes;\n // turn any internal arrays back into hashes for readable json\n if (layer == 0 && this.inputLookup) {\n nodes = Object.keys(this.inputLookup);\n }\n else if (layer == this.outputLayer && this.outputLookup) {\n nodes = Object.keys(this.outputLookup);\n }\n else {\n nodes = range(0, this.sizes[layer]);\n }\n\n for (let j = 0; j < nodes.length; j++) {\n let node = nodes[j];\n layers[layer][node] = {};\n\n if (layer > 0) {\n layers[layer][node].bias = this.biases[layer][j];\n layers[layer][node].weights = {};\n for (let k in layers[layer - 1]) {\n let index = k;\n if (layer == 1 && this.inputLookup) {\n index = this.inputLookup[k];\n }\n layers[layer][node].weights[k] = this.weights[layer][j][index];\n }\n }\n }\n }\n return { layers: layers, outputLookup:!!this.outputLookup, inputLookup:!!this.inputLookup };\n }\n\n /**\n *\n * @param json\n * @returns {NeuralNetwork}\n */\n fromJSON(json) {\n let size = json.layers.length;\n this.outputLayer = size - 1;\n\n this.sizes = new Array(size);\n this.weights = new Array(size);\n this.biases = new Array(size);\n this.outputs = new Array(size);\n\n for (let i = 0; i <= this.outputLayer; i++) {\n let layer = json.layers[i];\n if (i == 0 && (!layer[0] || json.inputLookup)) {\n this.inputLookup = lookup.lookupFromHash(layer);\n }\n else if (i == this.outputLayer && (!layer[0] || json.outputLookup)) {\n this.outputLookup = lookup.lookupFromHash(layer);\n }\n\n let nodes = Object.keys(layer);\n this.sizes[i] = nodes.length;\n this.weights[i] = [];\n this.biases[i] = [];\n this.outputs[i] = [];\n\n for (let j in nodes) {\n let node = nodes[j];\n this.biases[i][j] = layer[node].bias;\n this.weights[i][j] = toArray(layer[node].weights);\n }\n }\n return this;\n }\n\n /**\n *\n * @returns {Function}\n */\n toFunction() {\n const json = this.toJSON();\n const jsonString = JSON.stringify(json);\n // return standalone function that mimics run()\n return new Function('input', `\n var net = ${ jsonString };\n for (var i = 1; i < net.layers.length; i++) {\n var layer = net.layers[i];\n var output = {};\n \n for (var id in layer) {\n var node = layer[id];\n var sum = node.bias;\n \n for (var iid in node.weights) {\n sum += node.weights[iid] * input[iid];\n }\n output[id] = (1 / (1 + Math.exp(-sum)));\n }\n input = output;\n }\n return output;\n `);\n }\n\n /**\n * This will create a TrainStream (WriteStream) for us to send the training data to.\n * @param opts training options\n * @returns {TrainStream|*}\n */\n createTrainStream(opts) {\n opts = opts || {};\n opts.neuralNetwork = this;\n this.trainStream = new TrainStream(opts);\n return this.trainStream;\n }\n}\n"]} \ No newline at end of file +{"version":3,"sources":["../src/neural-network.js"],"names":["NeuralNetwork","options","learningRate","momentum","hiddenSizes","hiddenLayers","binaryThresh","sizes","outputLayer","biases","weights","outputs","deltas","changes","errors","keepNetworkIntact","length","layer","size","Array","node","prevSize","input","inputLookup","toArray","output","runInput","outputLookup","toHash","sum","k","Math","exp","data","formatData","iterations","errorThresh","log","console","logPeriod","callback","callbackPeriod","inputSize","outputSize","push","max","floor","forEach","unshift","initialize","error","i","j","err","trainPattern","target","calculateDeltas","adjustWeights","incoming","delta","change","constructor","tmp","datum","Float64Array","buildLookup","map","value","array","Object","assign","isBinary","falsePos","falseNeg","truePos","trueNeg","misclasses","actual","expected","indexOf","misclass","stats","total","precision","recall","accuracy","layers","nodes","keys","bias","index","json","lookupFromHash","toJSON","jsonString","JSON","stringify","Function","opts","neuralNetwork","trainStream"],"mappings":"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","file":"neural-network.js","sourcesContent":["import lookup from './lookup';\nimport TrainStream from './train-stream';\nimport max from './utilities/max';\nimport mse from './utilities/mse';\nimport randos from './utilities/randos';\nimport range from './utilities/range';\nimport toArray from './utilities/to-array';\nimport zeros from './utilities/zeros';\n\n/**\n *\n * @param {object} options\n * @constructor\n */\nexport default class NeuralNetwork {\n constructor(options) {\n options = options || {};\n this.learningRate = options.learningRate || 0.3;\n this.momentum = options.momentum || 0.1;\n this.hiddenSizes = options.hiddenLayers;\n\n this.binaryThresh = options.binaryThresh || 0.5;\n\n this.sizes = null;\n this.outputLayer = null;\n this.biases = null; // weights for bias nodes\n this.weights = null;\n this.outputs = null;\n\n // state for training\n this.deltas = null;\n this.changes = null; // for momentum\n this.errors = null;\n }\n\n /**\n *\n * @param {} sizes\n * @param {Boolean} keepNetworkIntact\n */\n initialize(sizes, keepNetworkIntact) {\n this.sizes = sizes;\n this.outputLayer = this.sizes.length - 1;\n\n if (!keepNetworkIntact) {\n this.biases = []; // weights for bias nodes\n this.weights = [];\n this.outputs = [];\n }\n\n // state for training\n this.deltas = [];\n this.changes = []; // for momentum\n this.errors = [];\n\n for (let layer = 0; layer <= this.outputLayer; layer++) {\n let size = this.sizes[layer];\n this.deltas[layer] = zeros(size);\n this.errors[layer] = zeros(size);\n if (!keepNetworkIntact) {\n this.outputs[layer] = zeros(size);\n }\n\n if (layer > 0) {\n this.biases[layer] = randos(size);\n if (!keepNetworkIntact) {\n this.weights[layer] = new Array(size);\n }\n this.changes[layer] = new Array(size);\n\n for (let node = 0; node < size; node++) {\n let prevSize = this.sizes[layer - 1];\n if (!keepNetworkIntact) {\n this.weights[layer][node] = randos(prevSize);\n }\n this.changes[layer][node] = zeros(prevSize);\n }\n }\n }\n }\n\n /**\n *\n * @param input\n * @returns {*}\n */\n run(input) {\n if (this.inputLookup) {\n input = lookup.toArray(this.inputLookup, input);\n }\n\n let output = this.runInput(input);\n\n if (this.outputLookup) {\n output = lookup.toHash(this.outputLookup, output);\n }\n return output;\n }\n\n /**\n *\n * @param input\n * @returns {*}\n */\n runInput(input) {\n this.outputs[0] = input; // set output state of input layer\n\n let output = null;\n for (let layer = 1; layer <= this.outputLayer; layer++) {\n for (let node = 0; node < this.sizes[layer]; node++) {\n let weights = this.weights[layer][node];\n\n let sum = this.biases[layer][node];\n for (let k = 0; k < weights.length; k++) {\n sum += weights[k] * input[k];\n }\n this.outputs[layer][node] = 1 / (1 + Math.exp(-sum));\n }\n output = input = this.outputs[layer];\n }\n return output;\n }\n\n /**\n *\n * @param data\n * @param options\n * @returns {{error: number, iterations: number}}\n */\n train(data, options = {}) {\n data = this.formatData(data);\n\n let iterations = options.iterations || 20000;\n let errorThresh = options.errorThresh || 0.005;\n let log = options.log ? (typeof options.log === 'function' ? options.log : console.log) : false;\n let logPeriod = options.logPeriod || 10;\n let learningRate = options.learningRate || this.learningRate || 0.3;\n let callback = options.callback;\n let callbackPeriod = options.callbackPeriod || 10;\n let sizes = [];\n let inputSize = data[0].input.length;\n let outputSize = data[0].output.length;\n let hiddenSizes = this.hiddenSizes;\n if (!hiddenSizes) {\n sizes.push(Math.max(3, Math.floor(inputSize / 2)));\n } else {\n hiddenSizes.forEach(size => {\n sizes.push(size);\n });\n }\n\n sizes.unshift(inputSize);\n sizes.push(outputSize);\n\n this.initialize(sizes, options.keepNetworkIntact);\n\n let error = 1;\n let i;\n for (i = 0; i < iterations && error > errorThresh; i++) {\n let sum = 0;\n for (let j = 0; j < data.length; j++) {\n let err = this.trainPattern(data[j].input, data[j].output, learningRate);\n sum += err;\n }\n error = sum / data.length;\n\n if (log && (i % logPeriod == 0)) {\n log('iterations:', i, 'training error:', error);\n }\n if (callback && (i % callbackPeriod == 0)) {\n callback({ error: error, iterations: i });\n }\n }\n\n return {\n error: error,\n iterations: i\n };\n }\n\n /**\n *\n * @param input\n * @param target\n * @param learningRate\n */\n trainPattern(input, target, learningRate) {\n learningRate = learningRate || this.learningRate;\n\n // forward propagate\n this.runInput(input);\n\n // back propagate\n this.calculateDeltas(target);\n this.adjustWeights(learningRate);\n\n let error = mse(this.errors[this.outputLayer]);\n return error;\n }\n\n /**\n *\n * @param target\n */\n calculateDeltas(target) {\n for (let layer = this.outputLayer; layer >= 0; layer--) {\n for (let node = 0; node < this.sizes[layer]; node++) {\n let output = this.outputs[layer][node];\n\n let error = 0;\n if (layer == this.outputLayer) {\n error = target[node] - output;\n }\n else {\n let deltas = this.deltas[layer + 1];\n for (let k = 0; k < deltas.length; k++) {\n error += deltas[k] * this.weights[layer + 1][k][node];\n }\n }\n this.errors[layer][node] = error;\n this.deltas[layer][node] = error * output * (1 - output);\n }\n }\n }\n\n /**\n *\n * @param learningRate\n */\n adjustWeights(learningRate) {\n for (let layer = 1; layer <= this.outputLayer; layer++) {\n let incoming = this.outputs[layer - 1];\n\n for (let node = 0; node < this.sizes[layer]; node++) {\n let delta = this.deltas[layer][node];\n\n for (let k = 0; k < incoming.length; k++) {\n let change = this.changes[layer][node][k];\n\n change = (learningRate * delta * incoming[k])\n + (this.momentum * change);\n\n this.changes[layer][node][k] = change;\n this.weights[layer][node][k] += change;\n }\n this.biases[layer][node] += learningRate * delta;\n }\n }\n }\n\n /**\n *\n * @param data\n * @returns {*}\n */\n formatData(data) {\n if (data.constructor !== Array) { // turn stream datum into array\n let tmp = [];\n tmp.push(data);\n data = tmp;\n }\n // turn sparse hash input into arrays with 0s as filler\n let datum = data[0].input;\n if (datum.constructor !== Array && !(datum instanceof Float64Array)) {\n if (!this.inputLookup) {\n this.inputLookup = lookup.buildLookup(data.map(value => value['input']));\n }\n data = data.map(datum => {\n let array = lookup.toArray(this.inputLookup, datum.input);\n return Object.assign({}, datum, { input: array });\n }, this);\n }\n\n if (data[0].output.constructor !== Array) {\n if (!this.outputLookup) {\n this.outputLookup = lookup.buildLookup(data.map(value => value['output']));\n }\n data = data.map(datum => {\n let array = lookup.toArray(this.outputLookup, datum.output);\n return Object.assign({}, datum, { output: array });\n }, this);\n }\n return data;\n }\n\n /**\n *\n * @param data\n * @returns {\n * {\n * error: number,\n * misclasses: Array\n * }\n * }\n */\n test(data) {\n data = this.formatData(data);\n\n // for binary classification problems with one output node\n let isBinary = data[0].output.length == 1;\n let falsePos = 0;\n let falseNeg = 0;\n let truePos = 0;\n let trueNeg = 0;\n\n // for classification problems\n let misclasses = [];\n\n // run each pattern through the trained network and collect\n // error and misclassification statistics\n let sum = 0;\n for (let i = 0; i < data.length; i++) {\n let output = this.runInput(data[i].input);\n let target = data[i].output;\n\n let actual, expected;\n if (isBinary) {\n actual = output[0] > this.binaryThresh ? 1 : 0;\n expected = target[0];\n }\n else {\n actual = output.indexOf(max(output));\n expected = target.indexOf(max(target));\n }\n\n if (actual != expected) {\n let misclass = data[i];\n Object.assign(misclass, {\n actual: actual,\n expected: expected\n });\n misclasses.push(misclass);\n }\n\n if (isBinary) {\n if (actual == 0 && expected == 0) {\n trueNeg++;\n }\n else if (actual == 1 && expected == 1) {\n truePos++;\n }\n else if (actual == 0 && expected == 1) {\n falseNeg++;\n }\n else if (actual == 1 && expected == 0) {\n falsePos++;\n }\n }\n\n let errors = output.map((value, i) => {\n return target[i] - value;\n });\n sum += mse(errors);\n }\n let error = sum / data.length;\n\n let stats = {\n error: error,\n misclasses: misclasses\n };\n\n if (isBinary) {\n Object.assign(stats, {\n trueNeg: trueNeg,\n truePos: truePos,\n falseNeg: falseNeg,\n falsePos: falsePos,\n total: data.length,\n precision: truePos / (truePos + falsePos),\n recall: truePos / (truePos + falseNeg),\n accuracy: (trueNeg + truePos) / data.length\n });\n }\n return stats;\n }\n\n /**\n *\n * @returns\n * {\n * layers: [\n * {\n * x: {},\n * y: {}\n * },\n * {\n * '0': {\n * bias: -0.98771313,\n * weights: {\n * x: 0.8374838,\n * y: 1.245858\n * },\n * '1': {\n * bias: 3.48192004,\n * weights: {\n * x: 1.7825821,\n * y: -2.67899\n * }\n * }\n * },\n * {\n * f: {\n * bias: 0.27205739,\n * weights: {\n * '0': 1.3161821,\n * '1': 2.00436\n * }\n * }\n * }\n * ]\n * }\n */\n toJSON() {\n let layers = [];\n for (let layer = 0; layer <= this.outputLayer; layer++) {\n layers[layer] = {};\n\n let nodes;\n // turn any internal arrays back into hashes for readable json\n if (layer == 0 && this.inputLookup) {\n nodes = Object.keys(this.inputLookup);\n }\n else if (layer == this.outputLayer && this.outputLookup) {\n nodes = Object.keys(this.outputLookup);\n }\n else {\n nodes = range(0, this.sizes[layer]);\n }\n\n for (let j = 0; j < nodes.length; j++) {\n let node = nodes[j];\n layers[layer][node] = {};\n\n if (layer > 0) {\n layers[layer][node].bias = this.biases[layer][j];\n layers[layer][node].weights = {};\n for (let k in layers[layer - 1]) {\n let index = k;\n if (layer == 1 && this.inputLookup) {\n index = this.inputLookup[k];\n }\n layers[layer][node].weights[k] = this.weights[layer][j][index];\n }\n }\n }\n }\n return { layers: layers, outputLookup:!!this.outputLookup, inputLookup:!!this.inputLookup };\n }\n\n /**\n *\n * @param json\n * @returns {NeuralNetwork}\n */\n fromJSON(json) {\n let size = json.layers.length;\n this.outputLayer = size - 1;\n\n this.sizes = new Array(size);\n this.weights = new Array(size);\n this.biases = new Array(size);\n this.outputs = new Array(size);\n\n for (let i = 0; i <= this.outputLayer; i++) {\n let layer = json.layers[i];\n if (i == 0 && (!layer[0] || json.inputLookup)) {\n this.inputLookup = lookup.lookupFromHash(layer);\n }\n else if (i == this.outputLayer && (!layer[0] || json.outputLookup)) {\n this.outputLookup = lookup.lookupFromHash(layer);\n }\n\n let nodes = Object.keys(layer);\n this.sizes[i] = nodes.length;\n this.weights[i] = [];\n this.biases[i] = [];\n this.outputs[i] = [];\n\n for (let j in nodes) {\n let node = nodes[j];\n this.biases[i][j] = layer[node].bias;\n this.weights[i][j] = toArray(layer[node].weights);\n }\n }\n return this;\n }\n\n /**\n *\n * @returns {Function}\n */\n toFunction() {\n const json = this.toJSON();\n const jsonString = JSON.stringify(json);\n // return standalone function that mimics run()\n return new Function('input', `\n var net = ${ jsonString };\n for (var i = 1; i < net.layers.length; i++) {\n var layer = net.layers[i];\n var output = {};\n \n for (var id in layer) {\n var node = layer[id];\n var sum = node.bias;\n \n for (var iid in node.weights) {\n sum += node.weights[iid] * input[iid];\n }\n output[id] = (1 / (1 + Math.exp(-sum)));\n }\n input = output;\n }\n return output;\n `);\n }\n\n /**\n * This will create a TrainStream (WriteStream) for us to send the training data to.\n * @param opts training options\n * @returns {TrainStream|*}\n */\n createTrainStream(opts) {\n opts = opts || {};\n opts.neuralNetwork = this;\n this.trainStream = new TrainStream(opts);\n return this.trainStream;\n }\n}\n"]} \ No newline at end of file diff --git a/dist/recurrent/gru.js b/dist/recurrent/gru.js new file mode 100644 index 000000000..116fa5d19 --- /dev/null +++ b/dist/recurrent/gru.js @@ -0,0 +1,107 @@ +'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; }; }(); + +var _matrix = require('./matrix'); + +var _matrix2 = _interopRequireDefault(_matrix); + +var _randomMatrix = require('./matrix/random-matrix'); + +var _randomMatrix2 = _interopRequireDefault(_randomMatrix); + +var _rnn = require('./rnn'); + +var _rnn2 = _interopRequireDefault(_rnn); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var GRU = function (_RNN) { + _inherits(GRU, _RNN); + + function GRU() { + _classCallCheck(this, GRU); + + return _possibleConstructorReturn(this, (GRU.__proto__ || Object.getPrototypeOf(GRU)).apply(this, arguments)); + } + + _createClass(GRU, [{ + key: 'getModel', + value: function getModel(hiddenSize, prevSize) { + return { + // update Gate + //wzxh + updateGateInputMatrix: new _randomMatrix2.default(hiddenSize, prevSize, 0.08), + //wzhh + updateGateHiddenMatrix: new _randomMatrix2.default(hiddenSize, hiddenSize, 0.08), + //bz + updateGateBias: new _matrix2.default(hiddenSize, 1), + + // reset Gate + //wrxh + resetGateInputMatrix: new _randomMatrix2.default(hiddenSize, prevSize, 0.08), + //wrhh + resetGateHiddenMatrix: new _randomMatrix2.default(hiddenSize, hiddenSize, 0.08), + //br + resetGateBias: new _matrix2.default(hiddenSize, 1), + + // cell write parameters + //wcxh + cellWriteInputMatrix: new _randomMatrix2.default(hiddenSize, prevSize, 0.08), + //wchh + cellWriteHiddenMatrix: new _randomMatrix2.default(hiddenSize, hiddenSize, 0.08), + //bc + cellWriteBias: new _matrix2.default(hiddenSize, 1) + }; + } + + /** + * + * @param {Equation} equation + * @param {Matrix} inputMatrix + * @param {Matrix} previousResult + * @param {Object} hiddenLayer + * @returns {Matrix} + */ + + }, { + key: 'getEquation', + value: function getEquation(equation, inputMatrix, previousResult, hiddenLayer) { + var sigmoid = equation.sigmoid.bind(equation); + var add = equation.add.bind(equation); + var multiply = equation.multiply.bind(equation); + var multiplyElement = equation.multiplyElement.bind(equation); + var tanh = equation.tanh.bind(equation); + var allOnes = equation.allOnes.bind(equation); + var cloneNegative = equation.cloneNegative.bind(equation); + + // update gate + var updateGate = sigmoid(add(add(multiply(hiddenLayer.updateGateInputMatrix, inputMatrix), multiply(hiddenLayer.updateGateHiddenMatrix, previousResult)), hiddenLayer.updateGateBias)); + + // reset gate + var resetGate = sigmoid(add(add(multiply(hiddenLayer.resetGateInputMatrix, inputMatrix), multiply(hiddenLayer.resetGateHiddenMatrix, previousResult)), hiddenLayer.resetGateBias)); + + // cell + var cell = tanh(add(add(multiply(hiddenLayer.cellWriteInputMatrix, inputMatrix), multiply(hiddenLayer.cellWriteHiddenMatrix, multiplyElement(resetGate, previousResult))), hiddenLayer.cellWriteBias)); + + // compute hidden state as gated, saturated cell activations + // negate updateGate + return add(multiplyElement(add(allOnes(updateGate.rows, updateGate.columns), cloneNegative(updateGate)), cell), multiplyElement(previousResult, updateGate)); + } + }]); + + return GRU; +}(_rnn2.default); + +exports.default = GRU; +//# sourceMappingURL=gru.js.map \ No newline at end of file diff --git a/dist/recurrent/gru.js.map b/dist/recurrent/gru.js.map new file mode 100644 index 000000000..e6814c2c9 --- /dev/null +++ b/dist/recurrent/gru.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../src/recurrent/gru.js"],"names":["GRU","hiddenSize","prevSize","updateGateInputMatrix","updateGateHiddenMatrix","updateGateBias","resetGateInputMatrix","resetGateHiddenMatrix","resetGateBias","cellWriteInputMatrix","cellWriteHiddenMatrix","cellWriteBias","equation","inputMatrix","previousResult","hiddenLayer","sigmoid","bind","add","multiply","multiplyElement","tanh","allOnes","cloneNegative","updateGate","resetGate","cell","rows","columns"],"mappings":";;;;;;;;AAAA;;;;AACA;;;;AACA;;;;;;;;;;;;IAEqBA,G;;;;;;;;;;;6BACVC,U,EAAYC,Q,EAAU;AAC7B,aAAO;AACL;AACA;AACAC,+BAAuB,2BAAiBF,UAAjB,EAA6BC,QAA7B,EAAuC,IAAvC,CAHlB;AAIL;AACAE,gCAAwB,2BAAiBH,UAAjB,EAA6BA,UAA7B,EAAyC,IAAzC,CALnB;AAML;AACAI,wBAAgB,qBAAWJ,UAAX,EAAuB,CAAvB,CAPX;;AASL;AACA;AACAK,8BAAsB,2BAAiBL,UAAjB,EAA6BC,QAA7B,EAAuC,IAAvC,CAXjB;AAYL;AACAK,+BAAuB,2BAAiBN,UAAjB,EAA6BA,UAA7B,EAAyC,IAAzC,CAblB;AAcL;AACAO,uBAAe,qBAAWP,UAAX,EAAuB,CAAvB,CAfV;;AAiBL;AACA;AACAQ,8BAAsB,2BAAiBR,UAAjB,EAA6BC,QAA7B,EAAuC,IAAvC,CAnBjB;AAoBL;AACAQ,+BAAuB,2BAAiBT,UAAjB,EAA6BA,UAA7B,EAAyC,IAAzC,CArBlB;AAsBL;AACAU,uBAAe,qBAAWV,UAAX,EAAuB,CAAvB;AAvBV,OAAP;AAyBD;;AAED;;;;;;;;;;;gCAQYW,Q,EAAUC,W,EAAaC,c,EAAgBC,W,EAAa;AAC9D,UAAIC,UAAUJ,SAASI,OAAT,CAAiBC,IAAjB,CAAsBL,QAAtB,CAAd;AACA,UAAIM,MAAMN,SAASM,GAAT,CAAaD,IAAb,CAAkBL,QAAlB,CAAV;AACA,UAAIO,WAAWP,SAASO,QAAT,CAAkBF,IAAlB,CAAuBL,QAAvB,CAAf;AACA,UAAIQ,kBAAkBR,SAASQ,eAAT,CAAyBH,IAAzB,CAA8BL,QAA9B,CAAtB;AACA,UAAIS,OAAOT,SAASS,IAAT,CAAcJ,IAAd,CAAmBL,QAAnB,CAAX;AACA,UAAIU,UAAUV,SAASU,OAAT,CAAiBL,IAAjB,CAAsBL,QAAtB,CAAd;AACA,UAAIW,gBAAgBX,SAASW,aAAT,CAAuBN,IAAvB,CAA4BL,QAA5B,CAApB;;AAEA;AACA,UAAIY,aAAaR,QACfE,IACEA,IACEC,SACEJ,YAAYZ,qBADd,EAEEU,WAFF,CADF,EAKEM,SACEJ,YAAYX,sBADd,EAEEU,cAFF,CALF,CADF,EAWEC,YAAYV,cAXd,CADe,CAAjB;;AAgBA;AACA,UAAIoB,YAAYT,QACZE,IACEA,IACEC,SACEJ,YAAYT,oBADd,EAEEO,WAFF,CADF,EAKEM,SACEJ,YAAYR,qBADd,EAEEO,cAFF,CALF,CADF,EAWEC,YAAYP,aAXd,CADY,CAAhB;;AAgBA;AACA,UAAIkB,OAAOL,KACTH,IACEA,IACEC,SACEJ,YAAYN,oBADd,EAEEI,WAFF,CADF,EAKEM,SACEJ,YAAYL,qBADd,EAEEU,gBACEK,SADF,EAEEX,cAFF,CAFF,CALF,CADF,EAcEC,YAAYJ,aAdd,CADS,CAAX;;AAmBA;AACA;AACA,aAAOO,IACLE,gBACEF,IACEI,QAAQE,WAAWG,IAAnB,EAAyBH,WAAWI,OAApC,CADF,EAEEL,cAAcC,UAAd,CAFF,CADF,EAKEE,IALF,CADK,EAQLN,gBACEN,cADF,EAEEU,UAFF,CARK,CAAP;AAaD;;;;;;kBAnHkBxB,G","file":"gru.js","sourcesContent":["import Matrix from './matrix';\nimport RandomMatrix from './matrix/random-matrix';\nimport RNN from './rnn';\n\nexport default class GRU extends RNN {\n getModel(hiddenSize, prevSize) {\n return {\n // update Gate\n //wzxh\n updateGateInputMatrix: new RandomMatrix(hiddenSize, prevSize, 0.08),\n //wzhh\n updateGateHiddenMatrix: new RandomMatrix(hiddenSize, hiddenSize, 0.08),\n //bz\n updateGateBias: new Matrix(hiddenSize, 1),\n\n // reset Gate\n //wrxh\n resetGateInputMatrix: new RandomMatrix(hiddenSize, prevSize, 0.08),\n //wrhh\n resetGateHiddenMatrix: new RandomMatrix(hiddenSize, hiddenSize, 0.08),\n //br\n resetGateBias: new Matrix(hiddenSize, 1),\n\n // cell write parameters\n //wcxh\n cellWriteInputMatrix: new RandomMatrix(hiddenSize, prevSize, 0.08),\n //wchh\n cellWriteHiddenMatrix: new RandomMatrix(hiddenSize, hiddenSize, 0.08),\n //bc\n cellWriteBias: new Matrix(hiddenSize, 1)\n };\n }\n\n /**\n *\n * @param {Equation} equation\n * @param {Matrix} inputMatrix\n * @param {Matrix} previousResult\n * @param {Object} hiddenLayer\n * @returns {Matrix}\n */\n getEquation(equation, inputMatrix, previousResult, hiddenLayer) {\n let sigmoid = equation.sigmoid.bind(equation);\n let add = equation.add.bind(equation);\n let multiply = equation.multiply.bind(equation);\n let multiplyElement = equation.multiplyElement.bind(equation);\n let tanh = equation.tanh.bind(equation);\n let allOnes = equation.allOnes.bind(equation);\n let cloneNegative = equation.cloneNegative.bind(equation);\n\n // update gate\n let updateGate = sigmoid(\n add(\n add(\n multiply(\n hiddenLayer.updateGateInputMatrix,\n inputMatrix\n ),\n multiply(\n hiddenLayer.updateGateHiddenMatrix,\n previousResult\n )\n ),\n hiddenLayer.updateGateBias\n )\n );\n\n // reset gate\n let resetGate = sigmoid(\n add(\n add(\n multiply(\n hiddenLayer.resetGateInputMatrix,\n inputMatrix\n ),\n multiply(\n hiddenLayer.resetGateHiddenMatrix,\n previousResult\n )\n ),\n hiddenLayer.resetGateBias\n )\n );\n\n // cell\n let cell = tanh(\n add(\n add(\n multiply(\n hiddenLayer.cellWriteInputMatrix,\n inputMatrix\n ),\n multiply(\n hiddenLayer.cellWriteHiddenMatrix,\n multiplyElement(\n resetGate,\n previousResult\n )\n )\n ),\n hiddenLayer.cellWriteBias\n )\n );\n\n // compute hidden state as gated, saturated cell activations\n // negate updateGate\n return add(\n multiplyElement(\n add(\n allOnes(updateGate.rows, updateGate.columns),\n cloneNegative(updateGate)\n ),\n cell\n ),\n multiplyElement(\n previousResult,\n updateGate\n )\n );\n }\n}\n"]} \ No newline at end of file diff --git a/dist/recurrent/lstm.js b/dist/recurrent/lstm.js new file mode 100644 index 000000000..0087159c5 --- /dev/null +++ b/dist/recurrent/lstm.js @@ -0,0 +1,116 @@ +'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; }; }(); + +var _matrix = require('./matrix'); + +var _matrix2 = _interopRequireDefault(_matrix); + +var _randomMatrix = require('./matrix/random-matrix'); + +var _randomMatrix2 = _interopRequireDefault(_randomMatrix); + +var _rnn = require('./rnn'); + +var _rnn2 = _interopRequireDefault(_rnn); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var LSTM = function (_RNN) { + _inherits(LSTM, _RNN); + + function LSTM() { + _classCallCheck(this, LSTM); + + return _possibleConstructorReturn(this, (LSTM.__proto__ || Object.getPrototypeOf(LSTM)).apply(this, arguments)); + } + + _createClass(LSTM, [{ + key: 'getModel', + value: function getModel(hiddenSize, prevSize) { + return { + // gates parameters + //wix + inputMatrix: new _randomMatrix2.default(hiddenSize, prevSize, 0.08), + //wih + inputHidden: new _randomMatrix2.default(hiddenSize, hiddenSize, 0.08), + //bi + inputBias: new _matrix2.default(hiddenSize, 1), + + //wfx + forgetMatrix: new _randomMatrix2.default(hiddenSize, prevSize, 0.08), + //wfh + forgetHidden: new _randomMatrix2.default(hiddenSize, hiddenSize, 0.08), + //bf + forgetBias: new _matrix2.default(hiddenSize, 1), + + //wox + outputMatrix: new _randomMatrix2.default(hiddenSize, prevSize, 0.08), + //woh + outputHidden: new _randomMatrix2.default(hiddenSize, hiddenSize, 0.08), + //bo + outputBias: new _matrix2.default(hiddenSize, 1), + + // cell write params + //wcx + cellActivationMatrix: new _randomMatrix2.default(hiddenSize, prevSize, 0.08), + //wch + cellActivationHidden: new _randomMatrix2.default(hiddenSize, hiddenSize, 0.08), + //bc + cellActivationBias: new _matrix2.default(hiddenSize, 1) + }; + } + + /** + * + * @param {Equation} equation + * @param {Matrix} inputMatrix + * @param {Matrix} previousResult + * @param {Object} hiddenLayer + * @returns {Matrix} + */ + + }, { + key: 'getEquation', + value: function getEquation(equation, inputMatrix, previousResult, hiddenLayer) { + var sigmoid = equation.sigmoid.bind(equation); + var add = equation.add.bind(equation); + var multiply = equation.multiply.bind(equation); + var multiplyElement = equation.multiplyElement.bind(equation); + var tanh = equation.tanh.bind(equation); + + var inputGate = sigmoid(add(add(multiply(hiddenLayer.inputMatrix, inputMatrix), multiply(hiddenLayer.inputHidden, previousResult)), hiddenLayer.inputBias)); + + var forgetGate = sigmoid(add(add(multiply(hiddenLayer.forgetMatrix, inputMatrix), multiply(hiddenLayer.forgetHidden, previousResult)), hiddenLayer.forgetBias)); + + // output gate + var outputGate = sigmoid(add(add(multiply(hiddenLayer.outputMatrix, inputMatrix), multiply(hiddenLayer.outputHidden, previousResult)), hiddenLayer.outputBias)); + + // write operation on cells + var cellWrite = tanh(add(add(multiply(hiddenLayer.cellActivationMatrix, inputMatrix), multiply(hiddenLayer.cellActivationHidden, previousResult)), hiddenLayer.cellActivationBias)); + + // compute new cell activation + var retainCell = multiplyElement(forgetGate, previousResult); // what do we keep from cell + var writeCell = multiplyElement(inputGate, cellWrite); // what do we write to cell + var cell = add(retainCell, writeCell); // new cell contents + + // compute hidden state as gated, saturated cell activations + return multiplyElement(outputGate, tanh(cell)); + } + }]); + + return LSTM; +}(_rnn2.default); + +exports.default = LSTM; +//# sourceMappingURL=lstm.js.map \ No newline at end of file diff --git a/dist/recurrent/lstm.js.map b/dist/recurrent/lstm.js.map new file mode 100644 index 000000000..7be660abd --- /dev/null +++ b/dist/recurrent/lstm.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../src/recurrent/lstm.js"],"names":["LSTM","hiddenSize","prevSize","inputMatrix","inputHidden","inputBias","forgetMatrix","forgetHidden","forgetBias","outputMatrix","outputHidden","outputBias","cellActivationMatrix","cellActivationHidden","cellActivationBias","equation","previousResult","hiddenLayer","sigmoid","bind","add","multiply","multiplyElement","tanh","inputGate","forgetGate","outputGate","cellWrite","retainCell","writeCell","cell"],"mappings":"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","file":"lstm.js","sourcesContent":["import Matrix from './matrix';\nimport RandomMatrix from './matrix/random-matrix';\nimport RNN from './rnn';\n\nexport default class LSTM extends RNN {\n getModel(hiddenSize, prevSize) {\n return {\n // gates parameters\n //wix\n inputMatrix: new RandomMatrix(hiddenSize, prevSize, 0.08),\n //wih\n inputHidden: new RandomMatrix(hiddenSize, hiddenSize, 0.08),\n //bi\n inputBias: new Matrix(hiddenSize, 1),\n\n //wfx\n forgetMatrix: new RandomMatrix(hiddenSize, prevSize, 0.08),\n //wfh\n forgetHidden: new RandomMatrix(hiddenSize, hiddenSize, 0.08),\n //bf\n forgetBias: new Matrix(hiddenSize, 1),\n\n //wox\n outputMatrix: new RandomMatrix(hiddenSize, prevSize, 0.08),\n //woh\n outputHidden: new RandomMatrix(hiddenSize, hiddenSize, 0.08),\n //bo\n outputBias: new Matrix(hiddenSize, 1),\n\n // cell write params\n //wcx\n cellActivationMatrix: new RandomMatrix(hiddenSize, prevSize, 0.08),\n //wch\n cellActivationHidden: new RandomMatrix(hiddenSize, hiddenSize, 0.08),\n //bc\n cellActivationBias: new Matrix(hiddenSize, 1)\n };\n }\n\n /**\n *\n * @param {Equation} equation\n * @param {Matrix} inputMatrix\n * @param {Matrix} previousResult\n * @param {Object} hiddenLayer\n * @returns {Matrix}\n */\n getEquation(equation, inputMatrix, previousResult, hiddenLayer) {\n let sigmoid = equation.sigmoid.bind(equation);\n let add = equation.add.bind(equation);\n let multiply = equation.multiply.bind(equation);\n let multiplyElement = equation.multiplyElement.bind(equation);\n let tanh = equation.tanh.bind(equation);\n\n let inputGate = sigmoid(\n add(\n add(\n multiply(\n hiddenLayer.inputMatrix,\n inputMatrix\n ),\n multiply(\n hiddenLayer.inputHidden,\n previousResult\n )\n ),\n hiddenLayer.inputBias\n )\n );\n\n let forgetGate = sigmoid(\n add(\n add(\n multiply(\n hiddenLayer.forgetMatrix,\n inputMatrix\n ),\n multiply(\n hiddenLayer.forgetHidden,\n previousResult\n )\n ),\n hiddenLayer.forgetBias\n )\n );\n\n // output gate\n let outputGate = sigmoid(\n add(\n add(\n multiply(\n hiddenLayer.outputMatrix,\n inputMatrix\n ),\n multiply(\n hiddenLayer.outputHidden,\n previousResult\n )\n ),\n hiddenLayer.outputBias\n )\n );\n\n // write operation on cells\n let cellWrite = tanh(\n add(\n add(\n multiply(\n hiddenLayer.cellActivationMatrix,\n inputMatrix\n ),\n multiply(\n hiddenLayer.cellActivationHidden,\n previousResult\n )\n ),\n hiddenLayer.cellActivationBias\n )\n );\n\n // compute new cell activation\n let retainCell = multiplyElement(forgetGate, previousResult); // what do we keep from cell\n let writeCell = multiplyElement(inputGate, cellWrite); // what do we write to cell\n let cell = add(retainCell, writeCell); // new cell contents\n\n // compute hidden state as gated, saturated cell activations\n return multiplyElement(\n outputGate,\n tanh(cell)\n );\n }\n}\n"]} \ No newline at end of file diff --git a/dist/recurrent/matrix/add-b.js b/dist/recurrent/matrix/add-b.js new file mode 100644 index 000000000..433d7a5a4 --- /dev/null +++ b/dist/recurrent/matrix/add-b.js @@ -0,0 +1,19 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = addB; +/** + * adds {from} recurrence to {left} and {right} recurrence + * @param {Matrix} product + * @param {Matrix} left + * @param {Matrix} right + */ +function addB(product, left, right) { + for (var i = 0, max = product.recurrence.length; i < max; i++) { + left.recurrence[i] += product.recurrence[i]; + right.recurrence[i] += product.recurrence[i]; + } +} +//# sourceMappingURL=add-b.js.map \ No newline at end of file diff --git a/dist/recurrent/matrix/add-b.js.map b/dist/recurrent/matrix/add-b.js.map new file mode 100644 index 000000000..ecdf9c55b --- /dev/null +++ b/dist/recurrent/matrix/add-b.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../../src/recurrent/matrix/add-b.js"],"names":["addB","product","left","right","i","max","recurrence","length"],"mappings":";;;;;kBAMwBA,I;AANxB;;;;;;AAMe,SAASA,IAAT,CAAcC,OAAd,EAAuBC,IAAvB,EAA6BC,KAA7B,EAAoC;AACjD,OAAI,IAAIC,IAAI,CAAR,EAAWC,MAAMJ,QAAQK,UAAR,CAAmBC,MAAxC,EAAgDH,IAAIC,GAApD,EAAyDD,GAAzD,EAA8D;AAC5DF,SAAKI,UAAL,CAAgBF,CAAhB,KAAsBH,QAAQK,UAAR,CAAmBF,CAAnB,CAAtB;AACAD,UAAMG,UAAN,CAAiBF,CAAjB,KAAuBH,QAAQK,UAAR,CAAmBF,CAAnB,CAAvB;AACD;AACF","file":"add-b.js","sourcesContent":["/**\n * adds {from} recurrence to {left} and {right} recurrence\n * @param {Matrix} product\n * @param {Matrix} left\n * @param {Matrix} right\n */\nexport default function addB(product, left, right) {\n for(let i = 0, max = product.recurrence.length; i < max; i++) {\n left.recurrence[i] += product.recurrence[i];\n right.recurrence[i] += product.recurrence[i];\n }\n}\n"]} \ No newline at end of file diff --git a/dist/recurrent/matrix/add.js b/dist/recurrent/matrix/add.js new file mode 100644 index 000000000..782297753 --- /dev/null +++ b/dist/recurrent/matrix/add.js @@ -0,0 +1,18 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = add; +/** + * add {left} and {right} matrix weights into {into} + * @param {Matrix} product + * @param {Matrix} left + * @param {Matrix} right + */ +function add(product, left, right) { + for (var i = 0, max = left.weights.length; i < max; i++) { + product.weights[i] = left.weights[i] + right.weights[i]; + } +} +//# sourceMappingURL=add.js.map \ No newline at end of file diff --git a/dist/recurrent/matrix/add.js.map b/dist/recurrent/matrix/add.js.map new file mode 100644 index 000000000..fd26e0042 --- /dev/null +++ b/dist/recurrent/matrix/add.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../../src/recurrent/matrix/add.js"],"names":["add","product","left","right","i","max","weights","length"],"mappings":";;;;;kBAMwBA,G;AANxB;;;;;;AAMe,SAASA,GAAT,CAAaC,OAAb,EAAsBC,IAAtB,EAA4BC,KAA5B,EAAmC;AAChD,OAAI,IAAIC,IAAI,CAAR,EAAWC,MAAMH,KAAKI,OAAL,CAAaC,MAAlC,EAA0CH,IAAIC,GAA9C,EAAmDD,GAAnD,EAAwD;AACtDH,YAAQK,OAAR,CAAgBF,CAAhB,IAAqBF,KAAKI,OAAL,CAAaF,CAAb,IAAkBD,MAAMG,OAAN,CAAcF,CAAd,CAAvC;AACD;AACF","file":"add.js","sourcesContent":["/**\n * add {left} and {right} matrix weights into {into}\n * @param {Matrix} product\n * @param {Matrix} left\n * @param {Matrix} right\n */\nexport default function add(product, left, right) {\n for(let i = 0, max = left.weights.length; i < max; i++) {\n product.weights[i] = left.weights[i] + right.weights[i];\n }\n}\n"]} \ No newline at end of file diff --git a/dist/recurrent/matrix/all-ones.js b/dist/recurrent/matrix/all-ones.js new file mode 100644 index 000000000..0967ba6e8 --- /dev/null +++ b/dist/recurrent/matrix/all-ones.js @@ -0,0 +1,17 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = allOnes; +/** + * makes matrix weights and recurrence all ones + * @param {Matrix} product + */ +function allOnes(product) { + for (var i = 0, max = product.weights.length; i < max; i++) { + product.weights[i] = 1; + product.recurrence[i] = 0; + } +} +//# sourceMappingURL=all-ones.js.map \ No newline at end of file diff --git a/dist/recurrent/matrix/all-ones.js.map b/dist/recurrent/matrix/all-ones.js.map new file mode 100644 index 000000000..4f888643f --- /dev/null +++ b/dist/recurrent/matrix/all-ones.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../../src/recurrent/matrix/all-ones.js"],"names":["allOnes","product","i","max","weights","length","recurrence"],"mappings":";;;;;kBAIwBA,O;AAJxB;;;;AAIe,SAASA,OAAT,CAAiBC,OAAjB,EAA0B;AACvC,OAAI,IAAIC,IAAI,CAAR,EAAWC,MAAMF,QAAQG,OAAR,CAAgBC,MAArC,EAA6CH,IAAIC,GAAjD,EAAsDD,GAAtD,EAA2D;AACzDD,YAAQG,OAAR,CAAgBF,CAAhB,IAAqB,CAArB;AACAD,YAAQK,UAAR,CAAmBJ,CAAnB,IAAwB,CAAxB;AACD;AACF","file":"all-ones.js","sourcesContent":["/**\n * makes matrix weights and recurrence all ones\n * @param {Matrix} product\n */\nexport default function allOnes(product) {\n for(let i = 0, max = product.weights.length; i < max; i++) {\n product.weights[i] = 1;\n product.recurrence[i] = 0;\n }\n}\n"]} \ No newline at end of file diff --git a/dist/recurrent/matrix/clone-negative.js b/dist/recurrent/matrix/clone-negative.js new file mode 100644 index 000000000..fe3b39ecb --- /dev/null +++ b/dist/recurrent/matrix/clone-negative.js @@ -0,0 +1,22 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = cloneNegative; +/** + * + * @param {Matrix} product + * @param {Matrix} left + */ +function cloneNegative(product, left) { + product.rows = parseInt(left.rows); + product.columns = parseInt(left.columns); + product.weights = left.weights.slice(0); + product.recurrence = left.recurrence.slice(0); + for (var i = 0, max = left.weights.length; i < max; i++) { + product.weights[i] = -left.weights[i]; + product.recurrence[i] = 0; + } +} +//# sourceMappingURL=clone-negative.js.map \ No newline at end of file diff --git a/dist/recurrent/matrix/clone-negative.js.map b/dist/recurrent/matrix/clone-negative.js.map new file mode 100644 index 000000000..571d25d72 --- /dev/null +++ b/dist/recurrent/matrix/clone-negative.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../../src/recurrent/matrix/clone-negative.js"],"names":["cloneNegative","product","left","rows","parseInt","columns","weights","slice","recurrence","i","max","length"],"mappings":";;;;;kBAKwBA,a;AALxB;;;;;AAKe,SAASA,aAAT,CAAuBC,OAAvB,EAAgCC,IAAhC,EAAsC;AACnDD,UAAQE,IAAR,GAAeC,SAASF,KAAKC,IAAd,CAAf;AACAF,UAAQI,OAAR,GAAkBD,SAASF,KAAKG,OAAd,CAAlB;AACAJ,UAAQK,OAAR,GAAkBJ,KAAKI,OAAL,CAAaC,KAAb,CAAmB,CAAnB,CAAlB;AACAN,UAAQO,UAAR,GAAqBN,KAAKM,UAAL,CAAgBD,KAAhB,CAAsB,CAAtB,CAArB;AACA,OAAK,IAAIE,IAAI,CAAR,EAAWC,MAAMR,KAAKI,OAAL,CAAaK,MAAnC,EAA2CF,IAAIC,GAA/C,EAAoDD,GAApD,EAAyD;AACvDR,YAAQK,OAAR,CAAgBG,CAAhB,IAAqB,CAACP,KAAKI,OAAL,CAAaG,CAAb,CAAtB;AACAR,YAAQO,UAAR,CAAmBC,CAAnB,IAAwB,CAAxB;AACD;AACF","file":"clone-negative.js","sourcesContent":["/**\n *\n * @param {Matrix} product\n * @param {Matrix} left\n */\nexport default function cloneNegative(product, left) {\n product.rows = parseInt(left.rows);\n product.columns = parseInt(left.columns);\n product.weights = left.weights.slice(0);\n product.recurrence = left.recurrence.slice(0);\n for (let i = 0, max = left.weights.length; i < max; i++) {\n product.weights[i] = -left.weights[i];\n product.recurrence[i] = 0;\n }\n}\n"]} \ No newline at end of file diff --git a/dist/recurrent/matrix/clone.js b/dist/recurrent/matrix/clone.js new file mode 100644 index 000000000..2bd9b5ab5 --- /dev/null +++ b/dist/recurrent/matrix/clone.js @@ -0,0 +1,26 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = clone; + +var _ = require('./'); + +var _2 = _interopRequireDefault(_); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * + * @param {Matrix} product + */ +function clone(product) { + var cloned = new _2.default(); + cloned.rows = parseInt(product.rows); + cloned.columns = parseInt(product.columns); + cloned.weights = product.weights.slice(0); + cloned.recurrence = product.recurrence.slice(0); + return cloned; +} +//# sourceMappingURL=clone.js.map \ No newline at end of file diff --git a/dist/recurrent/matrix/clone.js.map b/dist/recurrent/matrix/clone.js.map new file mode 100644 index 000000000..2da41eb93 --- /dev/null +++ b/dist/recurrent/matrix/clone.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../../src/recurrent/matrix/clone.js"],"names":["clone","product","cloned","rows","parseInt","columns","weights","slice","recurrence"],"mappings":";;;;;kBAMwBA,K;;AANxB;;;;;;AAEA;;;;AAIe,SAASA,KAAT,CAAeC,OAAf,EAAwB;AACrC,MAAIC,SAAS,gBAAb;AACAA,SAAOC,IAAP,GAAcC,SAASH,QAAQE,IAAjB,CAAd;AACAD,SAAOG,OAAP,GAAiBD,SAASH,QAAQI,OAAjB,CAAjB;AACAH,SAAOI,OAAP,GAAiBL,QAAQK,OAAR,CAAgBC,KAAhB,CAAsB,CAAtB,CAAjB;AACAL,SAAOM,UAAP,GAAoBP,QAAQO,UAAR,CAAmBD,KAAnB,CAAyB,CAAzB,CAApB;AACA,SAAOL,MAAP;AACD","file":"clone.js","sourcesContent":["import Matrix from './';\n\n/**\n *\n * @param {Matrix} product\n */\nexport default function clone(product) {\n let cloned = new Matrix();\n cloned.rows = parseInt(product.rows);\n cloned.columns = parseInt(product.columns);\n cloned.weights = product.weights.slice(0);\n cloned.recurrence = product.recurrence.slice(0);\n return cloned;\n}\n"]} \ No newline at end of file diff --git a/dist/recurrent/matrix/copy.js b/dist/recurrent/matrix/copy.js new file mode 100644 index 000000000..74e9c1c3f --- /dev/null +++ b/dist/recurrent/matrix/copy.js @@ -0,0 +1,18 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = copy; +/* + * + * @param {Matrix} product + * @param {Matrix} left + */ +function copy(product, left) { + product.rows = parseInt(left.rows); + product.columns = parseInt(left.columns); + product.weights = left.weights.slice(0); + product.recurrence = left.recurrence.slice(0); +} +//# sourceMappingURL=copy.js.map \ No newline at end of file diff --git a/dist/recurrent/matrix/copy.js.map b/dist/recurrent/matrix/copy.js.map new file mode 100644 index 000000000..9342996ad --- /dev/null +++ b/dist/recurrent/matrix/copy.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../../src/recurrent/matrix/copy.js"],"names":["copy","product","left","rows","parseInt","columns","weights","slice","recurrence"],"mappings":";;;;;kBAKwBA,I;AALxB;;;;;AAKe,SAASA,IAAT,CAAcC,OAAd,EAAuBC,IAAvB,EAA6B;AAC1CD,UAAQE,IAAR,GAAeC,SAASF,KAAKC,IAAd,CAAf;AACAF,UAAQI,OAAR,GAAkBD,SAASF,KAAKG,OAAd,CAAlB;AACAJ,UAAQK,OAAR,GAAkBJ,KAAKI,OAAL,CAAaC,KAAb,CAAmB,CAAnB,CAAlB;AACAN,UAAQO,UAAR,GAAqBN,KAAKM,UAAL,CAAgBD,KAAhB,CAAsB,CAAtB,CAArB;AACD","file":"copy.js","sourcesContent":["/*\n *\n * @param {Matrix} product\n * @param {Matrix} left\n */\nexport default function copy(product, left) {\n product.rows = parseInt(left.rows);\n product.columns = parseInt(left.columns);\n product.weights = left.weights.slice(0);\n product.recurrence = left.recurrence.slice(0);\n}\n"]} \ No newline at end of file diff --git a/dist/recurrent/matrix/equation.js b/dist/recurrent/matrix/equation.js new file mode 100644 index 000000000..4457cc990 --- /dev/null +++ b/dist/recurrent/matrix/equation.js @@ -0,0 +1,390 @@ +'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; }; }(); + +var _ = require('./'); + +var _2 = _interopRequireDefault(_); + +var _onesMatrix = require('./ones-matrix'); + +var _onesMatrix2 = _interopRequireDefault(_onesMatrix); + +var _copy = require('./copy'); + +var _copy2 = _interopRequireDefault(_copy); + +var _cloneNegative2 = require('./clone-negative'); + +var _cloneNegative3 = _interopRequireDefault(_cloneNegative2); + +var _add2 = require('./add'); + +var _add3 = _interopRequireDefault(_add2); + +var _addB = require('./add-b'); + +var _addB2 = _interopRequireDefault(_addB); + +var _allOnes2 = require('./all-ones'); + +var _allOnes3 = _interopRequireDefault(_allOnes2); + +var _multiply2 = require('./multiply'); + +var _multiply3 = _interopRequireDefault(_multiply2); + +var _multiplyB = require('./multiply-b'); + +var _multiplyB2 = _interopRequireDefault(_multiplyB); + +var _multiplyElement2 = require('./multiply-element'); + +var _multiplyElement3 = _interopRequireDefault(_multiplyElement2); + +var _multiplyElementB = require('./multiply-element-b'); + +var _multiplyElementB2 = _interopRequireDefault(_multiplyElementB); + +var _relu2 = require('./relu'); + +var _relu3 = _interopRequireDefault(_relu2); + +var _reluB = require('./relu-b'); + +var _reluB2 = _interopRequireDefault(_reluB); + +var _rowPluck = require('./row-pluck'); + +var _rowPluck2 = _interopRequireDefault(_rowPluck); + +var _rowPluckB = require('./row-pluck-b'); + +var _rowPluckB2 = _interopRequireDefault(_rowPluckB); + +var _sigmoid2 = require('./sigmoid'); + +var _sigmoid3 = _interopRequireDefault(_sigmoid2); + +var _sigmoidB = require('./sigmoid-b'); + +var _sigmoidB2 = _interopRequireDefault(_sigmoidB); + +var _tanh2 = require('./tanh'); + +var _tanh3 = _interopRequireDefault(_tanh2); + +var _tanhB = require('./tanh-b'); + +var _tanhB2 = _interopRequireDefault(_tanhB); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var Equation = function () { + function Equation() { + _classCallCheck(this, Equation); + + this.inputRow = 0; + this.states = []; + this.previousResults = []; + this.previousResultInputs = []; + this.allMatrices = []; + } + + /** + * connects two matrices together by add + * @param {Matrix} left + * @param {Matrix} right + * @returns {Matrix} + */ + + + _createClass(Equation, [{ + key: 'add', + value: function add(left, right) { + if (left.weights.length !== right.weights.length) { + throw new Error('misaligned matrices'); + } + var product = new _2.default(left.rows, left.columns); + this.allMatrices.push(product); + this.states.push({ + left: left, + right: right, + product: product, + forwardFn: _add3.default, + backpropagationFn: _addB2.default + }); + return product; + } + + /** + * + * @param {Number} rows + * @param {Number} columns + * @returns {Matrix} + */ + + }, { + key: 'allOnes', + value: function allOnes(rows, columns) { + var product = new _2.default(rows, columns); + this.allMatrices.push(product); + this.states.push({ + left: product, + product: product, + forwardFn: _allOnes3.default + }); + return product; + } + + /** + * + * @param {Matrix} m + * @returns {Matrix} + */ + + }, { + key: 'cloneNegative', + value: function cloneNegative(m) { + var product = new _2.default(m.rows, m.columns); + this.allMatrices.push(product); + this.states.push({ + left: m, + product: product, + forwardFn: _cloneNegative3.default + }); + return product; + } + + /** + * connects two matrices together by subtract + * @param {Matrix} left + * @param {Matrix} right + * @returns {Matrix} + */ + + }, { + key: 'subtract', + value: function subtract(left, right) { + if (left.weights.length !== right.weights.length) { + throw new Error('misaligned matrices'); + } + return this.add(this.add(this.allOnes(left.rows, left.columns), this.cloneNegative(left)), right); + } + + /** + * connects two matrices together by multiply + * @param {Matrix} left + * @param {Matrix} right + * @returns {Matrix} + */ + + }, { + key: 'multiply', + value: function multiply(left, right) { + if (left.columns !== right.rows) { + throw new Error('misaligned matrices'); + } + var product = new _2.default(left.rows, right.columns); + this.allMatrices.push(product); + this.states.push({ + left: left, + right: right, + product: product, + forwardFn: _multiply3.default, + backpropagationFn: _multiplyB2.default + }); + return product; + } + + /** + * connects two matrices together by multiplyElement + * @param {Matrix} left + * @param {Matrix} right + * @returns {Matrix} + */ + + }, { + key: 'multiplyElement', + value: function multiplyElement(left, right) { + if (left.weights.length !== right.weights.length) { + throw new Error('misaligned matrices'); + } + var product = new _2.default(left.rows, left.columns); + this.allMatrices.push(product); + this.states.push({ + left: left, + right: right, + product: product, + forwardFn: _multiplyElement3.default, + backpropagationFn: _multiplyElementB2.default + }); + return product; + } + + /** + * connects a matrix to relu + * @param {Matrix} m + * @returns {Matrix} + */ + + }, { + key: 'relu', + value: function relu(m) { + var product = new _2.default(m.rows, m.columns); + this.allMatrices.push(product); + this.states.push({ + left: m, + product: product, + forwardFn: _relu3.default, + backpropagationFn: _reluB2.default + }); + return product; + } + + /** + * connects a matrix via a row + * @param {Matrix} m + * @returns {Matrix} + */ + + }, { + key: 'inputMatrixToRow', + value: function inputMatrixToRow(m) { + var self = this; + var product = new _2.default(m.columns, 1); + this.allMatrices.push(product); + this.states.push({ + left: m, + get right() { + return self.inputRow; + }, + product: product, + forwardFn: _rowPluck2.default, + backpropagationFn: _rowPluckB2.default + }); + return product; + } + + /** + * connects a matrix to sigmoid + * @param {Matrix} m + * @returns {Matrix} + */ + + }, { + key: 'sigmoid', + value: function sigmoid(m) { + var product = new _2.default(m.rows, m.columns); + this.allMatrices.push(product); + this.states.push({ + left: m, + product: product, + forwardFn: _sigmoid3.default, + backpropagationFn: _sigmoidB2.default + }); + return product; + } + + /** + * connects a matrix to tanh + * @param {Matrix} m + * @returns {Matrix} + */ + + }, { + key: 'tanh', + value: function tanh(m) { + var product = new _2.default(m.rows, m.columns); + this.allMatrices.push(product); + this.states.push({ + left: m, + product: product, + forwardFn: _tanh3.default, + backpropagationFn: _tanhB2.default + }); + return product; + } + + /** + * + * @param m + * @returns {Matrix} + */ + + }, { + key: 'observe', + value: function observe(m) { + var iForward = 0; + var iBackpropagate = 0; + this.states.push({ + forwardFn: function forwardFn() { + iForward++; + }, + backpropagationFn: function backpropagationFn() { + iBackpropagate++; + } + }); + return m; + } + + /** + * @patam {Number} [rowIndex] + * @output {Matrix} + */ + + }, { + key: 'run', + value: function run() { + var rowIndex = arguments.length <= 0 || arguments[0] === undefined ? 0 : arguments[0]; + + this.inputRow = rowIndex; + var state = void 0; + for (var i = 0, max = this.states.length; i < max; i++) { + state = this.states[i]; + if (!state.hasOwnProperty('forwardFn')) { + continue; + } + state.forwardFn(state.product, state.left, state.right); + } + + return state.product; + } + + /** + * @patam {Number} [rowIndex] + * @output {Matrix} + */ + + }, { + key: 'runBackpropagate', + value: function runBackpropagate() { + var rowIndex = arguments.length <= 0 || arguments[0] === undefined ? 0 : arguments[0]; + + this.inputRow = rowIndex; + + var i = this.states.length; + var state = void 0; + while (i-- > 0) { + state = this.states[i]; + if (!state.hasOwnProperty('backpropagationFn')) { + continue; + } + state.backpropagationFn(state.product, state.left, state.right); + } + + return state.product; + } + }]); + + return Equation; +}(); + +exports.default = Equation; +//# sourceMappingURL=equation.js.map \ No newline at end of file diff --git a/dist/recurrent/matrix/equation.js.map b/dist/recurrent/matrix/equation.js.map new file mode 100644 index 000000000..a7c84a237 --- /dev/null +++ b/dist/recurrent/matrix/equation.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../../src/recurrent/matrix/equation.js"],"names":["Equation","inputRow","states","previousResults","previousResultInputs","allMatrices","left","right","weights","length","Error","product","rows","columns","push","forwardFn","backpropagationFn","m","add","allOnes","cloneNegative","self","iForward","iBackpropagate","rowIndex","state","i","max","hasOwnProperty"],"mappings":";;;;;;;;AAAA;;;;AACA;;;;AACA;;;;AACA;;;;AACA;;;;AACA;;;;AACA;;;;AACA;;;;AACA;;;;AACA;;;;AACA;;;;AACA;;;;AACA;;;;AACA;;;;AACA;;;;AACA;;;;AACA;;;;AACA;;;;AACA;;;;;;;;IAEqBA,Q;AACnB,sBAAc;AAAA;;AACZ,SAAKC,QAAL,GAAgB,CAAhB;AACA,SAAKC,MAAL,GAAc,EAAd;AACA,SAAKC,eAAL,GAAuB,EAAvB;AACA,SAAKC,oBAAL,GAA4B,EAA5B;AACA,SAAKC,WAAL,GAAmB,EAAnB;AACD;;AAED;;;;;;;;;;wBAMIC,I,EAAMC,K,EAAO;AACf,UAAID,KAAKE,OAAL,CAAaC,MAAb,KAAwBF,MAAMC,OAAN,CAAcC,MAA1C,EAAkD;AAChD,cAAM,IAAIC,KAAJ,CAAU,qBAAV,CAAN;AACD;AACD,UAAIC,UAAU,eAAWL,KAAKM,IAAhB,EAAsBN,KAAKO,OAA3B,CAAd;AACA,WAAKR,WAAL,CAAiBS,IAAjB,CAAsBH,OAAtB;AACA,WAAKT,MAAL,CAAYY,IAAZ,CAAiB;AACfR,cAAMA,IADS;AAEfC,eAAOA,KAFQ;AAGfI,iBAASA,OAHM;AAIfI,gCAJe;AAKfC;AALe,OAAjB;AAOA,aAAOL,OAAP;AACD;;AAED;;;;;;;;;4BAMQC,I,EAAMC,O,EAAS;AACrB,UAAIF,UAAU,eAAWC,IAAX,EAAiBC,OAAjB,CAAd;AACA,WAAKR,WAAL,CAAiBS,IAAjB,CAAsBH,OAAtB;AACA,WAAKT,MAAL,CAAYY,IAAZ,CAAiB;AACfR,cAAMK,OADS;AAEfA,iBAASA,OAFM;AAGfI;AAHe,OAAjB;AAKA,aAAOJ,OAAP;AACD;;AAED;;;;;;;;kCAKcM,C,EAAG;AACf,UAAIN,UAAU,eAAWM,EAAEL,IAAb,EAAmBK,EAAEJ,OAArB,CAAd;AACA,WAAKR,WAAL,CAAiBS,IAAjB,CAAsBH,OAAtB;AACA,WAAKT,MAAL,CAAYY,IAAZ,CAAiB;AACfR,cAAMW,CADS;AAEfN,iBAASA,OAFM;AAGfI;AAHe,OAAjB;AAKA,aAAOJ,OAAP;AACD;;AAED;;;;;;;;;6BAMSL,I,EAAMC,K,EAAO;AACpB,UAAID,KAAKE,OAAL,CAAaC,MAAb,KAAwBF,MAAMC,OAAN,CAAcC,MAA1C,EAAkD;AAChD,cAAM,IAAIC,KAAJ,CAAU,qBAAV,CAAN;AACD;AACD,aAAO,KAAKQ,GAAL,CAAS,KAAKA,GAAL,CAAS,KAAKC,OAAL,CAAab,KAAKM,IAAlB,EAAwBN,KAAKO,OAA7B,CAAT,EAAgD,KAAKO,aAAL,CAAmBd,IAAnB,CAAhD,CAAT,EAAoFC,KAApF,CAAP;AACD;;AAED;;;;;;;;;6BAMSD,I,EAAMC,K,EAAO;AACpB,UAAID,KAAKO,OAAL,KAAiBN,MAAMK,IAA3B,EAAiC;AAC/B,cAAM,IAAIF,KAAJ,CAAU,qBAAV,CAAN;AACD;AACD,UAAIC,UAAU,eAAWL,KAAKM,IAAhB,EAAsBL,MAAMM,OAA5B,CAAd;AACA,WAAKR,WAAL,CAAiBS,IAAjB,CAAsBH,OAAtB;AACA,WAAKT,MAAL,CAAYY,IAAZ,CAAiB;AACfR,cAAMA,IADS;AAEfC,eAAOA,KAFQ;AAGfI,iBAASA,OAHM;AAIfI,qCAJe;AAKfC;AALe,OAAjB;AAOA,aAAOL,OAAP;AACD;;AAED;;;;;;;;;oCAMgBL,I,EAAMC,K,EAAO;AAC3B,UAAID,KAAKE,OAAL,CAAaC,MAAb,KAAwBF,MAAMC,OAAN,CAAcC,MAA1C,EAAkD;AAChD,cAAM,IAAIC,KAAJ,CAAU,qBAAV,CAAN;AACD;AACD,UAAIC,UAAU,eAAWL,KAAKM,IAAhB,EAAsBN,KAAKO,OAA3B,CAAd;AACA,WAAKR,WAAL,CAAiBS,IAAjB,CAAsBH,OAAtB;AACA,WAAKT,MAAL,CAAYY,IAAZ,CAAiB;AACfR,cAAMA,IADS;AAEfC,eAAOA,KAFQ;AAGfI,iBAASA,OAHM;AAIfI,4CAJe;AAKfC;AALe,OAAjB;AAOA,aAAOL,OAAP;AACD;;AAED;;;;;;;;yBAKKM,C,EAAG;AACN,UAAIN,UAAU,eAAWM,EAAEL,IAAb,EAAmBK,EAAEJ,OAArB,CAAd;AACA,WAAKR,WAAL,CAAiBS,IAAjB,CAAsBH,OAAtB;AACA,WAAKT,MAAL,CAAYY,IAAZ,CAAiB;AACfR,cAAMW,CADS;AAEfN,iBAASA,OAFM;AAGfI,iCAHe;AAIfC;AAJe,OAAjB;AAMA,aAAOL,OAAP;AACD;;AAED;;;;;;;;qCAKiBM,C,EAAG;AAClB,UAAII,OAAO,IAAX;AACA,UAAIV,UAAU,eAAWM,EAAEJ,OAAb,EAAsB,CAAtB,CAAd;AACA,WAAKR,WAAL,CAAiBS,IAAjB,CAAsBH,OAAtB;AACA,WAAKT,MAAL,CAAYY,IAAZ,CAAiB;AACfR,cAAMW,CADS;AAEf,YAAIV,KAAJ,GAAa;AACX,iBAAOc,KAAKpB,QAAZ;AACD,SAJc;AAKfU,iBAASA,OALM;AAMfI,qCANe;AAOfC;AAPe,OAAjB;AASA,aAAOL,OAAP;AACD;;AAED;;;;;;;;4BAKQM,C,EAAG;AACT,UAAIN,UAAU,eAAWM,EAAEL,IAAb,EAAmBK,EAAEJ,OAArB,CAAd;AACA,WAAKR,WAAL,CAAiBS,IAAjB,CAAsBH,OAAtB;AACA,WAAKT,MAAL,CAAYY,IAAZ,CAAiB;AACfR,cAAMW,CADS;AAEfN,iBAASA,OAFM;AAGfI,oCAHe;AAIfC;AAJe,OAAjB;AAMA,aAAOL,OAAP;AACD;;AAED;;;;;;;;yBAKKM,C,EAAG;AACN,UAAIN,UAAU,eAAWM,EAAEL,IAAb,EAAmBK,EAAEJ,OAArB,CAAd;AACA,WAAKR,WAAL,CAAiBS,IAAjB,CAAsBH,OAAtB;AACA,WAAKT,MAAL,CAAYY,IAAZ,CAAiB;AACfR,cAAMW,CADS;AAEfN,iBAASA,OAFM;AAGfI,iCAHe;AAIfC;AAJe,OAAjB;AAMA,aAAOL,OAAP;AACD;;AAED;;;;;;;;4BAKQM,C,EAAG;AACT,UAAIK,WAAW,CAAf;AACA,UAAIC,iBAAiB,CAArB;AACA,WAAKrB,MAAL,CAAYY,IAAZ,CAAiB;AACfC,mBAAW,qBAAW;AACpBO;AACD,SAHc;AAIfN,2BAAmB,6BAAW;AAC5BO;AACD;AANc,OAAjB;AAQA,aAAON,CAAP;AACD;;AAED;;;;;;;0BAIkB;AAAA,UAAdO,QAAc,yDAAH,CAAG;;AAChB,WAAKvB,QAAL,GAAgBuB,QAAhB;AACA,UAAIC,cAAJ;AACA,WAAK,IAAIC,IAAI,CAAR,EAAWC,MAAM,KAAKzB,MAAL,CAAYO,MAAlC,EAA0CiB,IAAIC,GAA9C,EAAmDD,GAAnD,EAAwD;AACtDD,gBAAQ,KAAKvB,MAAL,CAAYwB,CAAZ,CAAR;AACA,YAAI,CAACD,MAAMG,cAAN,CAAqB,WAArB,CAAL,EAAwC;AACtC;AACD;AACDH,cAAMV,SAAN,CAAgBU,MAAMd,OAAtB,EAA+Bc,MAAMnB,IAArC,EAA2CmB,MAAMlB,KAAjD;AACD;;AAED,aAAOkB,MAAMd,OAAb;AACD;;AAED;;;;;;;uCAI+B;AAAA,UAAda,QAAc,yDAAH,CAAG;;AAC7B,WAAKvB,QAAL,GAAgBuB,QAAhB;;AAEA,UAAIE,IAAI,KAAKxB,MAAL,CAAYO,MAApB;AACA,UAAIgB,cAAJ;AACA,aAAOC,MAAM,CAAb,EAAgB;AACdD,gBAAQ,KAAKvB,MAAL,CAAYwB,CAAZ,CAAR;AACA,YAAI,CAACD,MAAMG,cAAN,CAAqB,mBAArB,CAAL,EAAgD;AAC9C;AACD;AACDH,cAAMT,iBAAN,CAAwBS,MAAMd,OAA9B,EAAuCc,MAAMnB,IAA7C,EAAmDmB,MAAMlB,KAAzD;AACD;;AAED,aAAOkB,MAAMd,OAAb;AACD;;;;;;kBAxPkBX,Q","file":"equation.js","sourcesContent":["import Matrix from './';\nimport OnesMatrix from './ones-matrix';\nimport copy from './copy';\nimport cloneNegative from './clone-negative';\nimport add from './add';\nimport addB from './add-b';\nimport allOnes from './all-ones';\nimport multiply from './multiply';\nimport multiplyB from './multiply-b';\nimport multiplyElement from './multiply-element';\nimport multiplyElementB from './multiply-element-b';\nimport relu from './relu';\nimport reluB from './relu-b';\nimport rowPluck from './row-pluck';\nimport rowPluckB from './row-pluck-b';\nimport sigmoid from './sigmoid';\nimport sigmoidB from './sigmoid-b';\nimport tanh from './tanh';\nimport tanhB from './tanh-b';\n\nexport default class Equation {\n constructor() {\n this.inputRow = 0;\n this.states = [];\n this.previousResults = [];\n this.previousResultInputs = [];\n this.allMatrices = [];\n }\n\n /**\n * connects two matrices together by add\n * @param {Matrix} left\n * @param {Matrix} right\n * @returns {Matrix}\n */\n add(left, right) {\n if (left.weights.length !== right.weights.length) {\n throw new Error('misaligned matrices');\n }\n let product = new Matrix(left.rows, left.columns);\n this.allMatrices.push(product);\n this.states.push({\n left: left,\n right: right,\n product: product,\n forwardFn: add,\n backpropagationFn: addB\n });\n return product;\n }\n\n /**\n *\n * @param {Number} rows\n * @param {Number} columns\n * @returns {Matrix}\n */\n allOnes(rows, columns) {\n let product = new Matrix(rows, columns);\n this.allMatrices.push(product);\n this.states.push({\n left: product,\n product: product,\n forwardFn: allOnes\n });\n return product;\n }\n\n /**\n *\n * @param {Matrix} m\n * @returns {Matrix}\n */\n cloneNegative(m) {\n let product = new Matrix(m.rows, m.columns);\n this.allMatrices.push(product);\n this.states.push({\n left: m,\n product: product,\n forwardFn: cloneNegative\n });\n return product;\n }\n\n /**\n * connects two matrices together by subtract\n * @param {Matrix} left\n * @param {Matrix} right\n * @returns {Matrix}\n */\n subtract(left, right) {\n if (left.weights.length !== right.weights.length) {\n throw new Error('misaligned matrices');\n }\n return this.add(this.add(this.allOnes(left.rows, left.columns), this.cloneNegative(left)), right);\n }\n\n /**\n * connects two matrices together by multiply\n * @param {Matrix} left\n * @param {Matrix} right\n * @returns {Matrix}\n */\n multiply(left, right) {\n if (left.columns !== right.rows) {\n throw new Error('misaligned matrices');\n }\n let product = new Matrix(left.rows, right.columns);\n this.allMatrices.push(product);\n this.states.push({\n left: left,\n right: right,\n product: product,\n forwardFn: multiply,\n backpropagationFn: multiplyB\n });\n return product;\n }\n\n /**\n * connects two matrices together by multiplyElement\n * @param {Matrix} left\n * @param {Matrix} right\n * @returns {Matrix}\n */\n multiplyElement(left, right) {\n if (left.weights.length !== right.weights.length) {\n throw new Error('misaligned matrices');\n }\n let product = new Matrix(left.rows, left.columns);\n this.allMatrices.push(product);\n this.states.push({\n left: left,\n right: right,\n product: product,\n forwardFn: multiplyElement,\n backpropagationFn: multiplyElementB\n });\n return product;\n }\n\n /**\n * connects a matrix to relu\n * @param {Matrix} m\n * @returns {Matrix}\n */\n relu(m) {\n let product = new Matrix(m.rows, m.columns);\n this.allMatrices.push(product);\n this.states.push({\n left: m,\n product: product,\n forwardFn: relu,\n backpropagationFn: reluB\n });\n return product;\n }\n\n /**\n * connects a matrix via a row\n * @param {Matrix} m\n * @returns {Matrix}\n */\n inputMatrixToRow(m) {\n let self = this;\n let product = new Matrix(m.columns, 1);\n this.allMatrices.push(product);\n this.states.push({\n left: m,\n get right () {\n return self.inputRow;\n },\n product: product,\n forwardFn: rowPluck,\n backpropagationFn: rowPluckB\n });\n return product;\n }\n\n /**\n * connects a matrix to sigmoid\n * @param {Matrix} m\n * @returns {Matrix}\n */\n sigmoid(m) {\n let product = new Matrix(m.rows, m.columns);\n this.allMatrices.push(product);\n this.states.push({\n left: m,\n product: product,\n forwardFn: sigmoid,\n backpropagationFn: sigmoidB\n });\n return product;\n }\n\n /**\n * connects a matrix to tanh\n * @param {Matrix} m\n * @returns {Matrix}\n */\n tanh(m) {\n let product = new Matrix(m.rows, m.columns);\n this.allMatrices.push(product);\n this.states.push({\n left: m,\n product: product,\n forwardFn: tanh,\n backpropagationFn: tanhB\n });\n return product;\n }\n\n /**\n *\n * @param m\n * @returns {Matrix}\n */\n observe(m) {\n let iForward = 0;\n let iBackpropagate = 0;\n this.states.push({\n forwardFn: function() {\n iForward++;\n },\n backpropagationFn: function() {\n iBackpropagate++;\n }\n });\n return m;\n }\n\n /**\n * @patam {Number} [rowIndex]\n * @output {Matrix}\n */\n run(rowIndex = 0) {\n this.inputRow = rowIndex;\n let state;\n for (let i = 0, max = this.states.length; i < max; i++) {\n state = this.states[i];\n if (!state.hasOwnProperty('forwardFn')) {\n continue;\n }\n state.forwardFn(state.product, state.left, state.right);\n }\n\n return state.product;\n }\n\n /**\n * @patam {Number} [rowIndex]\n * @output {Matrix}\n */\n runBackpropagate(rowIndex = 0) {\n this.inputRow = rowIndex;\n\n let i = this.states.length;\n let state;\n while (i-- > 0) {\n state = this.states[i];\n if (!state.hasOwnProperty('backpropagationFn')) {\n continue;\n }\n state.backpropagationFn(state.product, state.left, state.right);\n }\n\n return state.product;\n }\n}\n"]} \ No newline at end of file diff --git a/dist/recurrent/matrix/index.js b/dist/recurrent/matrix/index.js new file mode 100644 index 000000000..e2a63809f --- /dev/null +++ b/dist/recurrent/matrix/index.js @@ -0,0 +1,145 @@ +'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; }; }(); + +var _zeros = require('../../utilities/zeros'); + +var _zeros2 = _interopRequireDefault(_zeros); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +/** + * A matrix + * @param {Number} [rows] + * @param {Number} [columns] + * @constructor + */ +var Matrix = function () { + function Matrix(rows, columns) { + _classCallCheck(this, Matrix); + + if (typeof rows === 'undefined') return; + if (typeof columns === 'undefined') return; + + this.rows = rows; + this.columns = columns; + this.weights = (0, _zeros2.default)(rows * columns); + this.recurrence = (0, _zeros2.default)(rows * columns); + } + + /** + * + * @param {Number} row + * @param {Number} col + * @returns {Float64Array|Array} + */ + + + _createClass(Matrix, [{ + key: 'getWeights', + value: function getWeights(row, col) { + // slow but careful accessor function + // we want row-major order + var ix = this.columns * row + col; + if (ix < 0 && ix >= this.weights.length) throw new Error('get accessor is skewed'); + return this.weights[ix]; + } + + /** + * + * @param {Number} row + * @param {Number} col + * @param v + * @returns {Matrix} + */ + + }, { + key: 'setWeight', + value: function setWeight(row, col, v) { + // slow but careful accessor function + var ix = this.columns * row + col; + if (ix < 0 && ix >= this.weights.length) throw new Error('set accessor is skewed'); + this.weights[ix] = v; + } + + /** + * + * @param {Number} row + * @param {Number} col + * @param v + * @returns {Matrix} + */ + + }, { + key: 'setRecurrence', + value: function setRecurrence(row, col, v) { + // slow but careful accessor function + var ix = this.columns * row + col; + if (ix < 0 && ix >= this.weights.length) throw new Error('set accessor is skewed'); + this.recurrence[ix] = v; + } + + /** + * + * @returns {{rows: *, columns: *, weights: Array}} + */ + + }, { + key: 'toJSON', + value: function toJSON() { + return { + rows: this.rows, + columns: this.columns, + weights: this.weights.slice(0) + }; + } + }], [{ + key: 'fromJSON', + value: function fromJSON(json) { + var matrix = new Matrix(json.rows, json.columns); + for (var i = 0, max = json.rows * json.columns; i < max; i++) { + matrix.weights[i] = json.weights[i]; // copy over weights + } + return matrix; + } + + /** + * + * @param weightRows + * @param [recurrenceRows] + * @returns {Matrix} + */ + + }, { + key: 'fromArray', + value: function fromArray(weightRows, recurrenceRows) { + var rows = weightRows.length; + var columns = weightRows[0].length; + var m = new Matrix(rows, columns); + + recurrenceRows = recurrenceRows || weightRows; + + for (var rowIndex = 0; rowIndex < rows; rowIndex++) { + var weightValues = weightRows[rowIndex]; + var recurrentValues = recurrenceRows[rowIndex]; + for (var columnIndex = 0; columnIndex < columns; columnIndex++) { + m.setWeight(rowIndex, columnIndex, weightValues[columnIndex]); + m.setRecurrence(rowIndex, columnIndex, recurrentValues[columnIndex]); + } + } + + return m; + } + }]); + + return Matrix; +}(); + +exports.default = Matrix; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/recurrent/matrix/index.js.map b/dist/recurrent/matrix/index.js.map new file mode 100644 index 000000000..0e8b5f890 --- /dev/null +++ b/dist/recurrent/matrix/index.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../../src/recurrent/matrix/index.js"],"names":["Matrix","rows","columns","weights","recurrence","row","col","ix","length","Error","v","slice","json","matrix","i","max","weightRows","recurrenceRows","m","rowIndex","weightValues","recurrentValues","columnIndex","setWeight","setRecurrence"],"mappings":"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","file":"index.js","sourcesContent":["import zeros from '../../utilities/zeros';\n\n/**\n * A matrix\n * @param {Number} [rows]\n * @param {Number} [columns]\n * @constructor\n */\nexport default class Matrix {\n constructor(rows, columns) {\n if (typeof rows === 'undefined') return;\n if (typeof columns === 'undefined') return;\n\n this.rows = rows;\n this.columns = columns;\n this.weights = zeros(rows * columns);\n this.recurrence = zeros(rows * columns);\n }\n\n /**\n *\n * @param {Number} row\n * @param {Number} col\n * @returns {Float64Array|Array}\n */\n getWeights(row, col) {\n // slow but careful accessor function\n // we want row-major order\n let ix = (this.columns * row) + col;\n if (ix < 0 && ix >= this.weights.length) throw new Error('get accessor is skewed');\n return this.weights[ix];\n }\n\n /**\n *\n * @param {Number} row\n * @param {Number} col\n * @param v\n * @returns {Matrix}\n */\n setWeight(row, col, v) {\n // slow but careful accessor function\n let ix = (this.columns * row) + col;\n if (ix < 0 && ix >= this.weights.length) throw new Error('set accessor is skewed');\n this.weights[ix] = v;\n }\n\n /**\n *\n * @param {Number} row\n * @param {Number} col\n * @param v\n * @returns {Matrix}\n */\n setRecurrence(row, col, v) {\n // slow but careful accessor function\n let ix = (this.columns * row) + col;\n if (ix < 0 && ix >= this.weights.length) throw new Error('set accessor is skewed');\n this.recurrence[ix] = v;\n }\n\n /**\n *\n * @returns {{rows: *, columns: *, weights: Array}}\n */\n toJSON() {\n return {\n rows: this.rows,\n columns: this.columns,\n weights: this.weights.slice(0)\n };\n }\n\n static fromJSON(json) {\n let matrix = new Matrix(json.rows, json.columns);\n for (let i = 0, max = json.rows * json.columns; i < max; i++) {\n matrix.weights[i] = json.weights[i]; // copy over weights\n }\n return matrix;\n }\n\n /**\n *\n * @param weightRows\n * @param [recurrenceRows]\n * @returns {Matrix}\n */\n static fromArray(weightRows, recurrenceRows) {\n var rows = weightRows.length;\n var columns = weightRows[0].length;\n var m = new Matrix(rows, columns);\n\n recurrenceRows = recurrenceRows || weightRows;\n\n for (var rowIndex = 0; rowIndex < rows; rowIndex++) {\n var weightValues = weightRows[rowIndex];\n var recurrentValues = recurrenceRows[rowIndex];\n for (var columnIndex = 0; columnIndex < columns; columnIndex++) {\n m.setWeight(rowIndex, columnIndex, weightValues[columnIndex]);\n m.setRecurrence(rowIndex, columnIndex, recurrentValues[columnIndex]);\n }\n }\n\n return m;\n }\n}\n"]} \ No newline at end of file diff --git a/dist/recurrent/matrix/max-i.js b/dist/recurrent/matrix/max-i.js new file mode 100644 index 000000000..5d71b3605 --- /dev/null +++ b/dist/recurrent/matrix/max-i.js @@ -0,0 +1,26 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = maxI; +/** + * + * @param {Matrix} m + * @returns {number} + */ +function maxI(m) { + // argmax of array w + var w = m.weights; + var maxv = w[0]; + var maxix = 0; + for (var i = 1, max = w.length; i < max; i++) { + var v = w[i]; + if (v < maxv) continue; + + maxix = i; + maxv = v; + } + return maxix; +}; +//# sourceMappingURL=max-i.js.map \ No newline at end of file diff --git a/dist/recurrent/matrix/max-i.js.map b/dist/recurrent/matrix/max-i.js.map new file mode 100644 index 000000000..8ae5d51dd --- /dev/null +++ b/dist/recurrent/matrix/max-i.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../../src/recurrent/matrix/max-i.js"],"names":["maxI","m","w","weights","maxv","maxix","i","max","length","v"],"mappings":";;;;;kBAKwBA,I;AALxB;;;;;AAKe,SAASA,IAAT,CAAcC,CAAd,EAAiB;AAC9B;AACA,MAAIC,IAAID,EAAEE,OAAV;AACA,MAAIC,OAAOF,EAAE,CAAF,CAAX;AACA,MAAIG,QAAQ,CAAZ;AACA,OAAK,IAAIC,IAAI,CAAR,EAAWC,MAAML,EAAEM,MAAxB,EAAgCF,IAAIC,GAApC,EAAyCD,GAAzC,EAA8C;AAC5C,QAAIG,IAAIP,EAAEI,CAAF,CAAR;AACA,QAAIG,IAAIL,IAAR,EAAc;;AAEdC,YAAQC,CAAR;AACAF,WAAOK,CAAP;AACD;AACD,SAAOJ,KAAP;AACD","file":"max-i.js","sourcesContent":["/**\n *\n * @param {Matrix} m\n * @returns {number}\n */\nexport default function maxI(m) {\n // argmax of array w\n let w = m.weights;\n let maxv = w[0];\n let maxix = 0;\n for (let i = 1, max = w.length; i < max; i++) {\n let v = w[i];\n if (v < maxv) continue;\n\n maxix = i;\n maxv = v;\n }\n return maxix;\n};\n"]} \ No newline at end of file diff --git a/dist/recurrent/matrix/multiply-b.js b/dist/recurrent/matrix/multiply-b.js new file mode 100644 index 000000000..95276a368 --- /dev/null +++ b/dist/recurrent/matrix/multiply-b.js @@ -0,0 +1,33 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = multiplyB; +/** + * multiplies {from} recurrence to {left} and {right} + * @param {Matrix} product + * @param {Matrix} left + * @param {Matrix} right + */ +function multiplyB(product, left, right) { + var leftRows = left.rows; + var leftColumns = left.columns; + var rightColumns = right.columns; + + // loop over rows of left + for (var leftRow = 0; leftRow < leftRows; leftRow++) { + + // loop over cols of right + for (var rightColumn = 0; rightColumn < rightColumns; rightColumn++) { + + //loop over columns of left + for (var leftColumn = 0; leftColumn < leftColumns; leftColumn++) { + var backPropagateValue = product.recurrence[rightColumns * leftRow + rightColumn]; + left.recurrence[leftColumns * leftRow + leftColumn] += right.weights[rightColumns * leftColumn + rightColumn] * backPropagateValue; + right.recurrence[rightColumns * leftColumn + rightColumn] += left.weights[leftColumns * leftRow + leftColumn] * backPropagateValue; + } + } + } +} +//# sourceMappingURL=multiply-b.js.map \ No newline at end of file diff --git a/dist/recurrent/matrix/multiply-b.js.map b/dist/recurrent/matrix/multiply-b.js.map new file mode 100644 index 000000000..da4c24bc0 --- /dev/null +++ b/dist/recurrent/matrix/multiply-b.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../../src/recurrent/matrix/multiply-b.js"],"names":["multiplyB","product","left","right","leftRows","rows","leftColumns","columns","rightColumns","leftRow","rightColumn","leftColumn","backPropagateValue","recurrence","weights"],"mappings":";;;;;kBAMwBA,S;AANxB;;;;;;AAMe,SAASA,SAAT,CAAmBC,OAAnB,EAA4BC,IAA5B,EAAkCC,KAAlC,EAAyC;AACtD,MAAIC,WAAWF,KAAKG,IAApB;AACA,MAAIC,cAAcJ,KAAKK,OAAvB;AACA,MAAIC,eAAeL,MAAMI,OAAzB;;AAEA;AACA,OAAI,IAAIE,UAAU,CAAlB,EAAqBA,UAAUL,QAA/B,EAAyCK,SAAzC,EAAoD;;AAElD;AACA,SAAI,IAAIC,cAAc,CAAtB,EAAyBA,cAAcF,YAAvC,EAAqDE,aAArD,EAAoE;;AAElE;AACA,WAAI,IAAIC,aAAa,CAArB,EAAwBA,aAAaL,WAArC,EAAkDK,YAAlD,EAAgE;AAC9D,YAAIC,qBAAqBX,QAAQY,UAAR,CAAmBL,eAAeC,OAAf,GAAyBC,WAA5C,CAAzB;AACAR,aAAKW,UAAL,CAAgBP,cAAcG,OAAd,GAAwBE,UAAxC,KAAuDR,MAAMW,OAAN,CAAcN,eAAeG,UAAf,GAA4BD,WAA1C,IAAyDE,kBAAhH;AACAT,cAAMU,UAAN,CAAiBL,eAAeG,UAAf,GAA4BD,WAA7C,KAA6DR,KAAKY,OAAL,CAAaR,cAAcG,OAAd,GAAwBE,UAArC,IAAmDC,kBAAhH;AACD;AACF;AACF;AACF","file":"multiply-b.js","sourcesContent":["/**\n * multiplies {from} recurrence to {left} and {right}\n * @param {Matrix} product\n * @param {Matrix} left\n * @param {Matrix} right\n */\nexport default function multiplyB(product, left, right) {\n let leftRows = left.rows;\n let leftColumns = left.columns;\n let rightColumns = right.columns;\n\n // loop over rows of left\n for(let leftRow = 0; leftRow < leftRows; leftRow++) {\n\n // loop over cols of right\n for(let rightColumn = 0; rightColumn < rightColumns; rightColumn++) {\n\n //loop over columns of left\n for(let leftColumn = 0; leftColumn < leftColumns; leftColumn++) {\n let backPropagateValue = product.recurrence[rightColumns * leftRow + rightColumn];\n left.recurrence[leftColumns * leftRow + leftColumn] += right.weights[rightColumns * leftColumn + rightColumn] * backPropagateValue;\n right.recurrence[rightColumns * leftColumn + rightColumn] += left.weights[leftColumns * leftRow + leftColumn] * backPropagateValue;\n }\n }\n }\n}\n"]} \ No newline at end of file diff --git a/dist/recurrent/matrix/multiply-element-b.js b/dist/recurrent/matrix/multiply-element-b.js new file mode 100644 index 000000000..135434491 --- /dev/null +++ b/dist/recurrent/matrix/multiply-element-b.js @@ -0,0 +1,19 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = multiplyElementB; +/** + * multiplies {left} and {right} weight by {from} recurrence into {left} and {right} recurrence + * @param {Matrix} product + * @param {Matrix} left + * @param {Matrix} right + */ +function multiplyElementB(product, left, right) { + for (var i = 0, weights = left.weights.length; i < weights; i++) { + left.recurrence[i] += right.weights[i] * product.recurrence[i]; + right.recurrence[i] += left.weights[i] * product.recurrence[i]; + } +} +//# sourceMappingURL=multiply-element-b.js.map \ No newline at end of file diff --git a/dist/recurrent/matrix/multiply-element-b.js.map b/dist/recurrent/matrix/multiply-element-b.js.map new file mode 100644 index 000000000..0d72fe4ff --- /dev/null +++ b/dist/recurrent/matrix/multiply-element-b.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../../src/recurrent/matrix/multiply-element-b.js"],"names":["multiplyElementB","product","left","right","i","weights","length","recurrence"],"mappings":";;;;;kBAMwBA,gB;AANxB;;;;;;AAMe,SAASA,gBAAT,CAA0BC,OAA1B,EAAmCC,IAAnC,EAAyCC,KAAzC,EAAgD;AAC7D,OAAI,IAAIC,IAAI,CAAR,EAAWC,UAAUH,KAAKG,OAAL,CAAaC,MAAtC,EAA8CF,IAAIC,OAAlD,EAA2DD,GAA3D,EAAgE;AAC9DF,SAAKK,UAAL,CAAgBH,CAAhB,KAAsBD,MAAME,OAAN,CAAcD,CAAd,IAAmBH,QAAQM,UAAR,CAAmBH,CAAnB,CAAzC;AACAD,UAAMI,UAAN,CAAiBH,CAAjB,KAAuBF,KAAKG,OAAL,CAAaD,CAAb,IAAkBH,QAAQM,UAAR,CAAmBH,CAAnB,CAAzC;AACD;AACF","file":"multiply-element-b.js","sourcesContent":["/**\n * multiplies {left} and {right} weight by {from} recurrence into {left} and {right} recurrence\n * @param {Matrix} product\n * @param {Matrix} left\n * @param {Matrix} right\n */\nexport default function multiplyElementB(product, left, right) {\n for(let i = 0, weights = left.weights.length; i < weights; i++) {\n left.recurrence[i] += right.weights[i] * product.recurrence[i];\n right.recurrence[i] += left.weights[i] * product.recurrence[i];\n }\n}\n"]} \ No newline at end of file diff --git a/dist/recurrent/matrix/multiply-element.js b/dist/recurrent/matrix/multiply-element.js new file mode 100644 index 000000000..6af4634ac --- /dev/null +++ b/dist/recurrent/matrix/multiply-element.js @@ -0,0 +1,17 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = multiplyElement; +/** + * @param {Matrix} product + * @param {Matrix} left + * @param {Matrix} right + */ +function multiplyElement(product, left, right) { + for (var i = 0, weights = left.weights.length; i < weights; i++) { + product.weights[i] = left.weights[i] * right.weights[i]; + } +} +//# sourceMappingURL=multiply-element.js.map \ No newline at end of file diff --git a/dist/recurrent/matrix/multiply-element.js.map b/dist/recurrent/matrix/multiply-element.js.map new file mode 100644 index 000000000..2c5086a65 --- /dev/null +++ b/dist/recurrent/matrix/multiply-element.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../../src/recurrent/matrix/multiply-element.js"],"names":["multiplyElement","product","left","right","i","weights","length"],"mappings":";;;;;kBAKwBA,e;AALxB;;;;;AAKe,SAASA,eAAT,CAAyBC,OAAzB,EAAkCC,IAAlC,EAAwCC,KAAxC,EAA+C;AAC5D,OAAI,IAAIC,IAAI,CAAR,EAAWC,UAAUH,KAAKG,OAAL,CAAaC,MAAtC,EAA8CF,IAAIC,OAAlD,EAA2DD,GAA3D,EAAgE;AAC9DH,YAAQI,OAAR,CAAgBD,CAAhB,IAAqBF,KAAKG,OAAL,CAAaD,CAAb,IAAkBD,MAAME,OAAN,CAAcD,CAAd,CAAvC;AACD;AACF","file":"multiply-element.js","sourcesContent":["/**\n * @param {Matrix} product\n * @param {Matrix} left\n * @param {Matrix} right\n */\nexport default function multiplyElement(product, left, right) {\n for(let i = 0, weights = left.weights.length; i < weights; i++) {\n product.weights[i] = left.weights[i] * right.weights[i];\n }\n}\n"]} \ No newline at end of file diff --git a/dist/recurrent/matrix/multiply.js b/dist/recurrent/matrix/multiply.js new file mode 100644 index 000000000..4c28e8d2e --- /dev/null +++ b/dist/recurrent/matrix/multiply.js @@ -0,0 +1,36 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = multiply; +/** + * multiply {left} and {right} matrix weights to {into} + * @param {Matrix} product + * @param {Matrix} left + * @param {Matrix} right + */ +function multiply(product, left, right) { + var leftRows = left.rows; + var leftColumns = left.columns; + var rightColumns = right.columns; + + // loop over rows of left + for (var leftRow = 0; leftRow < leftRows; leftRow++) { + + // loop over cols of right + for (var rightColumn = 0; rightColumn < rightColumns; rightColumn++) { + + // dot product loop + var dot = 0; + + //loop over columns of left + for (var leftColumn = 0; leftColumn < leftColumns; leftColumn++) { + dot += left.weights[leftColumns * leftRow + leftColumn] * right.weights[rightColumns * leftColumn + rightColumn]; + } + var i = rightColumns * leftRow + rightColumn; + product.weights[i] = dot; + } + } +} +//# sourceMappingURL=multiply.js.map \ No newline at end of file diff --git a/dist/recurrent/matrix/multiply.js.map b/dist/recurrent/matrix/multiply.js.map new file mode 100644 index 000000000..e304dc39f --- /dev/null +++ b/dist/recurrent/matrix/multiply.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../../src/recurrent/matrix/multiply.js"],"names":["multiply","product","left","right","leftRows","rows","leftColumns","columns","rightColumns","leftRow","rightColumn","dot","leftColumn","weights","i"],"mappings":";;;;;kBAMwBA,Q;AANxB;;;;;;AAMe,SAASA,QAAT,CAAkBC,OAAlB,EAA2BC,IAA3B,EAAiCC,KAAjC,EAAwC;AACrD,MAAIC,WAAWF,KAAKG,IAApB;AACA,MAAIC,cAAcJ,KAAKK,OAAvB;AACA,MAAIC,eAAeL,MAAMI,OAAzB;;AAEA;AACA,OAAI,IAAIE,UAAU,CAAlB,EAAqBA,UAAUL,QAA/B,EAAyCK,SAAzC,EAAoD;;AAElD;AACA,SAAI,IAAIC,cAAc,CAAtB,EAAyBA,cAAcF,YAAvC,EAAqDE,aAArD,EAAoE;;AAElE;AACA,UAAIC,MAAM,CAAV;;AAEA;AACA,WAAI,IAAIC,aAAa,CAArB,EAAwBA,aAAaN,WAArC,EAAkDM,YAAlD,EAAgE;AAC9DD,eACIT,KAAKW,OAAL,CAAaP,cAAcG,OAAd,GAAwBG,UAArC,IACAT,MAAMU,OAAN,CAAcL,eAAeI,UAAf,GAA4BF,WAA1C,CAFJ;AAGD;AACD,UAAII,IAAIN,eAAeC,OAAf,GAAyBC,WAAjC;AACAT,cAAQY,OAAR,CAAgBC,CAAhB,IAAqBH,GAArB;AACD;AACF;AACF","file":"multiply.js","sourcesContent":["/**\n * multiply {left} and {right} matrix weights to {into}\n * @param {Matrix} product\n * @param {Matrix} left\n * @param {Matrix} right\n */\nexport default function multiply(product, left, right) {\n let leftRows = left.rows;\n let leftColumns = left.columns;\n let rightColumns = right.columns;\n\n // loop over rows of left\n for(let leftRow = 0; leftRow < leftRows; leftRow++) {\n\n // loop over cols of right\n for(let rightColumn = 0; rightColumn < rightColumns; rightColumn++) {\n\n // dot product loop\n let dot = 0;\n\n //loop over columns of left\n for(let leftColumn = 0; leftColumn < leftColumns; leftColumn++) {\n dot +=\n left.weights[leftColumns * leftRow + leftColumn]\n * right.weights[rightColumns * leftColumn + rightColumn];\n }\n let i = rightColumns * leftRow + rightColumn;\n product.weights[i] = dot;\n }\n }\n}\n"]} \ No newline at end of file diff --git a/dist/recurrent/matrix/ones-matrix.js b/dist/recurrent/matrix/ones-matrix.js new file mode 100644 index 000000000..d58251eb1 --- /dev/null +++ b/dist/recurrent/matrix/ones-matrix.js @@ -0,0 +1,47 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _ = require('./'); + +var _2 = _interopRequireDefault(_); + +var _ones = require('../../utilities/ones'); + +var _ones2 = _interopRequireDefault(_ones); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +/** return Matrix but filled with random numbers from gaussian + * @param {Number} [rows] + * @param {Number} [columns] + * @constructor + */ +var OnesMatrix = function (_Matrix) { + _inherits(OnesMatrix, _Matrix); + + function OnesMatrix(rows, columns) { + _classCallCheck(this, OnesMatrix); + + var _this = _possibleConstructorReturn(this, (OnesMatrix.__proto__ || Object.getPrototypeOf(OnesMatrix)).call(this, rows, columns)); + + _this.rows = rows; + _this.columns = columns; + _this.weights = (0, _ones2.default)(rows * columns); + _this.recurrence = (0, _ones2.default)(rows * columns); + return _this; + } + + return OnesMatrix; +}(_2.default); + +exports.default = OnesMatrix; +//# sourceMappingURL=ones-matrix.js.map \ No newline at end of file diff --git a/dist/recurrent/matrix/ones-matrix.js.map b/dist/recurrent/matrix/ones-matrix.js.map new file mode 100644 index 000000000..4c066cbd5 --- /dev/null +++ b/dist/recurrent/matrix/ones-matrix.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../../src/recurrent/matrix/ones-matrix.js"],"names":["OnesMatrix","rows","columns","weights","recurrence"],"mappings":";;;;;;AAAA;;;;AACA;;;;;;;;;;;;AAEA;;;;;IAKqBA,U;;;AACnB,sBAAYC,IAAZ,EAAkBC,OAAlB,EAA2B;AAAA;;AAAA,wHACnBD,IADmB,EACbC,OADa;;AAEzB,UAAKD,IAAL,GAAYA,IAAZ;AACA,UAAKC,OAAL,GAAeA,OAAf;AACA,UAAKC,OAAL,GAAe,oBAAKF,OAAOC,OAAZ,CAAf;AACA,UAAKE,UAAL,GAAkB,oBAAKH,OAAOC,OAAZ,CAAlB;AALyB;AAM1B;;;;;kBAPkBF,U","file":"ones-matrix.js","sourcesContent":["import Matrix from './';\nimport ones from '../../utilities/ones';\n\n/** return Matrix but filled with random numbers from gaussian\n * @param {Number} [rows]\n * @param {Number} [columns]\n * @constructor\n */\nexport default class OnesMatrix extends Matrix {\n constructor(rows, columns) {\n super(rows, columns);\n this.rows = rows;\n this.columns = columns;\n this.weights = ones(rows * columns);\n this.recurrence = ones(rows * columns);\n }\n}\n"]} \ No newline at end of file diff --git a/dist/recurrent/matrix/random-matrix.js b/dist/recurrent/matrix/random-matrix.js new file mode 100644 index 000000000..0e648f687 --- /dev/null +++ b/dist/recurrent/matrix/random-matrix.js @@ -0,0 +1,48 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _ = require('./'); + +var _2 = _interopRequireDefault(_); + +var _random = require('../../utilities/random'); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +/** return Matrix but filled with random numbers from gaussian + * @param {Number} [rows] + * @param {Number} [columns] + * @param std + * @constructor + */ +var RandomMatrix = function (_Matrix) { + _inherits(RandomMatrix, _Matrix); + + function RandomMatrix(rows, columns, std) { + _classCallCheck(this, RandomMatrix); + + var _this = _possibleConstructorReturn(this, (RandomMatrix.__proto__ || Object.getPrototypeOf(RandomMatrix)).call(this, rows, columns)); + + _this.rows = rows; + _this.columns = columns; + _this.std = std; + for (var i = 0, max = _this.weights.length; i < max; i++) { + _this.weights[i] = (0, _random.randomF)(-std, std); + } + return _this; + } + + return RandomMatrix; +}(_2.default); + +exports.default = RandomMatrix; +//# sourceMappingURL=random-matrix.js.map \ No newline at end of file diff --git a/dist/recurrent/matrix/random-matrix.js.map b/dist/recurrent/matrix/random-matrix.js.map new file mode 100644 index 000000000..400c1bc53 --- /dev/null +++ b/dist/recurrent/matrix/random-matrix.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../../src/recurrent/matrix/random-matrix.js"],"names":["RandomMatrix","rows","columns","std","i","max","weights","length"],"mappings":";;;;;;AAAA;;;;AACA;;;;;;;;;;AAEA;;;;;;IAMqBA,Y;;;AACnB,wBAAYC,IAAZ,EAAkBC,OAAlB,EAA2BC,GAA3B,EAAgC;AAAA;;AAAA,4HACxBF,IADwB,EAClBC,OADkB;;AAE9B,UAAKD,IAAL,GAAYA,IAAZ;AACA,UAAKC,OAAL,GAAeA,OAAf;AACA,UAAKC,GAAL,GAAWA,GAAX;AACA,SAAI,IAAIC,IAAI,CAAR,EAAWC,MAAM,MAAKC,OAAL,CAAaC,MAAlC,EAA0CH,IAAIC,GAA9C,EAAmDD,GAAnD,EAAwD;AACtD,YAAKE,OAAL,CAAaF,CAAb,IAAkB,qBAAQ,CAACD,GAAT,EAAcA,GAAd,CAAlB;AACD;AAP6B;AAQ/B;;;;;kBATkBH,Y","file":"random-matrix.js","sourcesContent":["import Matrix from './';\nimport { randomF } from '../../utilities/random';\n\n/** return Matrix but filled with random numbers from gaussian\n * @param {Number} [rows]\n * @param {Number} [columns]\n * @param std\n * @constructor\n */\nexport default class RandomMatrix extends Matrix {\n constructor(rows, columns, std) {\n super(rows, columns);\n this.rows = rows;\n this.columns = columns;\n this.std = std;\n for(let i = 0, max = this.weights.length; i < max; i++) {\n this.weights[i] = randomF(-std, std);\n }\n }\n}\n"]} \ No newline at end of file diff --git a/dist/recurrent/matrix/random-n-matrix.js b/dist/recurrent/matrix/random-n-matrix.js new file mode 100644 index 000000000..4e047c6d1 --- /dev/null +++ b/dist/recurrent/matrix/random-n-matrix.js @@ -0,0 +1,58 @@ +'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; }; }(); + +var _ = require('./'); + +var _2 = _interopRequireDefault(_); + +var _random = require('../../utilities/random'); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +/** + * + * @param {Number} rows + * @param {Number} columns + * @param mu + * @param std + * @constructor + */ +var _class = function (_Matrix) { + _inherits(_class, _Matrix); + + function _class(rows, columns, mu, std) { + _classCallCheck(this, _class); + + var _this = _possibleConstructorReturn(this, (_class.__proto__ || Object.getPrototypeOf(_class)).call(this, rows, columns)); + + _this.fillRandN(mu, std); + return _this; + } + // fill matrix with random gaussian numbers + + + _createClass(_class, [{ + key: 'fillRandN', + value: function fillRandN(mu, std) { + for (var i = 0, max = this.weights.length; i < max; i++) { + this.weights[i] = (0, _random.randomN)(mu, std); + } + } + }]); + + return _class; +}(_2.default); + +exports.default = _class; +//# sourceMappingURL=random-n-matrix.js.map \ No newline at end of file diff --git a/dist/recurrent/matrix/random-n-matrix.js.map b/dist/recurrent/matrix/random-n-matrix.js.map new file mode 100644 index 000000000..017239529 --- /dev/null +++ b/dist/recurrent/matrix/random-n-matrix.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../../src/recurrent/matrix/random-n-matrix.js"],"names":["rows","columns","mu","std","fillRandN","i","max","weights","length"],"mappings":";;;;;;;;AAAA;;;;AACA;;;;;;;;;;AACA;;;;;;;;;;;AASE,kBAAYA,IAAZ,EAAkBC,OAAlB,EAA2BC,EAA3B,EAA+BC,GAA/B,EAAoC;AAAA;;AAAA,gHAC5BH,IAD4B,EACtBC,OADsB;;AAElC,UAAKG,SAAL,CAAeF,EAAf,EAAmBC,GAAnB;AAFkC;AAGnC;AACD;;;;;8BACUD,E,EAAIC,G,EAAK;AACjB,WAAI,IAAIE,IAAI,CAAR,EAAWC,MAAM,KAAKC,OAAL,CAAaC,MAAlC,EAA0CH,IAAIC,GAA9C,EAAmDD,GAAnD,EAAwD;AACtD,aAAKE,OAAL,CAAaF,CAAb,IAAkB,qBAAQH,EAAR,EAAYC,GAAZ,CAAlB;AACD;AACF","file":"random-n-matrix.js","sourcesContent":["import Matrix from './';\nimport { randomN } from '../../utilities/random';\n/**\n *\n * @param {Number} rows\n * @param {Number} columns\n * @param mu\n * @param std\n * @constructor\n */\nexport default class extends Matrix {\n constructor(rows, columns, mu, std) {\n super(rows, columns);\n this.fillRandN(mu, std);\n }\n // fill matrix with random gaussian numbers\n fillRandN(mu, std) {\n for(let i = 0, max = this.weights.length; i < max; i++) {\n this.weights[i] = randomN(mu, std);\n }\n }\n}\n"]} \ No newline at end of file diff --git a/dist/recurrent/matrix/relu-b.js b/dist/recurrent/matrix/relu-b.js new file mode 100644 index 000000000..de79a3e14 --- /dev/null +++ b/dist/recurrent/matrix/relu-b.js @@ -0,0 +1,17 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = reluB; +/** + * adds {from} recurrence to {m} recurrence when {m} weights are above other a threshold of 0 + * @param {Matrix} product + * @param {Matrix} m + */ +function reluB(product, left) { + for (var i = 0, max = product.recurrence.length; i < max; i++) { + left.recurrence[i] += left.weights[i] > 0 ? product.recurrence[i] : 0; + } +} +//# sourceMappingURL=relu-b.js.map \ No newline at end of file diff --git a/dist/recurrent/matrix/relu-b.js.map b/dist/recurrent/matrix/relu-b.js.map new file mode 100644 index 000000000..945492443 --- /dev/null +++ b/dist/recurrent/matrix/relu-b.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../../src/recurrent/matrix/relu-b.js"],"names":["reluB","product","left","i","max","recurrence","length","weights"],"mappings":";;;;;kBAKwBA,K;AALxB;;;;;AAKe,SAASA,KAAT,CAAeC,OAAf,EAAwBC,IAAxB,EAA8B;AAC3C,OAAI,IAAIC,IAAI,CAAR,EAAWC,MAAMH,QAAQI,UAAR,CAAmBC,MAAxC,EAAgDH,IAAIC,GAApD,EAAyDD,GAAzD,EAA8D;AAC5DD,SAAKG,UAAL,CAAgBF,CAAhB,KAAsBD,KAAKK,OAAL,CAAaJ,CAAb,IAAkB,CAAlB,GAAsBF,QAAQI,UAAR,CAAmBF,CAAnB,CAAtB,GAA8C,CAApE;AACD;AACF","file":"relu-b.js","sourcesContent":["/**\n * adds {from} recurrence to {m} recurrence when {m} weights are above other a threshold of 0\n * @param {Matrix} product\n * @param {Matrix} m\n */\nexport default function reluB(product, left) {\n for(let i = 0, max = product.recurrence.length; i < max; i++) {\n left.recurrence[i] += left.weights[i] > 0 ? product.recurrence[i] : 0;\n }\n}\n"]} \ No newline at end of file diff --git a/dist/recurrent/matrix/relu.js b/dist/recurrent/matrix/relu.js new file mode 100644 index 000000000..941ae2f6e --- /dev/null +++ b/dist/recurrent/matrix/relu.js @@ -0,0 +1,18 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = relu; +/** + * + * relu {m} weights to {into} weights + * @param {Matrix} product + * @param {Matrix} left + */ +function relu(product, left) { + for (var i = 0, max = left.weights.length; i < max; i++) { + product.weights[i] = Math.max(0, left.weights[i]); // relu + } +} +//# sourceMappingURL=relu.js.map \ No newline at end of file diff --git a/dist/recurrent/matrix/relu.js.map b/dist/recurrent/matrix/relu.js.map new file mode 100644 index 000000000..d21c9700b --- /dev/null +++ b/dist/recurrent/matrix/relu.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../../src/recurrent/matrix/relu.js"],"names":["relu","product","left","i","max","weights","length","Math"],"mappings":";;;;;kBAMwBA,I;AANxB;;;;;;AAMe,SAASA,IAAT,CAAcC,OAAd,EAAuBC,IAAvB,EAA6B;AAC1C,OAAI,IAAIC,IAAI,CAAR,EAAWC,MAAMF,KAAKG,OAAL,CAAaC,MAAlC,EAA0CH,IAAIC,GAA9C,EAAmDD,GAAnD,EAAwD;AACtDF,YAAQI,OAAR,CAAgBF,CAAhB,IAAqBI,KAAKH,GAAL,CAAS,CAAT,EAAYF,KAAKG,OAAL,CAAaF,CAAb,CAAZ,CAArB,CADsD,CACH;AACpD;AACF","file":"relu.js","sourcesContent":["/**\n *\n * relu {m} weights to {into} weights\n * @param {Matrix} product\n * @param {Matrix} left\n */\nexport default function relu(product, left) {\n for(let i = 0, max = left.weights.length; i < max; i++) {\n product.weights[i] = Math.max(0, left.weights[i]); // relu\n }\n}\n"]} \ No newline at end of file diff --git a/dist/recurrent/matrix/row-pluck-b.js b/dist/recurrent/matrix/row-pluck-b.js new file mode 100644 index 000000000..aacbca1e8 --- /dev/null +++ b/dist/recurrent/matrix/row-pluck-b.js @@ -0,0 +1,18 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = rowPluckB; +/** + * adds {from} recurrence into {m} recurrence + * @param {Matrix} product + * @param {Matrix} left + * @param {Number} rowIndex + */ +function rowPluckB(product, left, rowIndex) { + for (var column = 0, columns = left.columns; column < columns; column++) { + left.recurrence[columns * rowIndex + column] += product.recurrence[column]; + } +} +//# sourceMappingURL=row-pluck-b.js.map \ No newline at end of file diff --git a/dist/recurrent/matrix/row-pluck-b.js.map b/dist/recurrent/matrix/row-pluck-b.js.map new file mode 100644 index 000000000..85459282d --- /dev/null +++ b/dist/recurrent/matrix/row-pluck-b.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../../src/recurrent/matrix/row-pluck-b.js"],"names":["rowPluckB","product","left","rowIndex","column","columns","recurrence"],"mappings":";;;;;kBAMwBA,S;AANxB;;;;;;AAMe,SAASA,SAAT,CAAmBC,OAAnB,EAA4BC,IAA5B,EAAkCC,QAAlC,EAA4C;AACzD,OAAK,IAAIC,SAAS,CAAb,EAAgBC,UAAUH,KAAKG,OAApC,EAA6CD,SAASC,OAAtD,EAA+DD,QAA/D,EAAyE;AACvEF,SAAKI,UAAL,CAAgBD,UAAUF,QAAV,GAAqBC,MAArC,KAAgDH,QAAQK,UAAR,CAAmBF,MAAnB,CAAhD;AACD;AACF","file":"row-pluck-b.js","sourcesContent":["/**\n * adds {from} recurrence into {m} recurrence\n * @param {Matrix} product\n * @param {Matrix} left\n * @param {Number} rowIndex\n */\nexport default function rowPluckB(product, left, rowIndex) {\n for (let column = 0, columns = left.columns; column < columns; column++) {\n left.recurrence[columns * rowIndex + column] += product.recurrence[column];\n }\n}\n"]} \ No newline at end of file diff --git a/dist/recurrent/matrix/row-pluck.js b/dist/recurrent/matrix/row-pluck.js new file mode 100644 index 000000000..1085a1983 --- /dev/null +++ b/dist/recurrent/matrix/row-pluck.js @@ -0,0 +1,17 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = rowPluck; +/** + * @param {Matrix} product + * @param {Matrix} left + * @param {Number} rowPluckIndex + */ +function rowPluck(product, left, rowPluckIndex) { + for (var column = 0, columns = left.columns; column < columns; column++) { + product.weights[column] = left.weights[columns * rowPluckIndex + column]; + } +} +//# sourceMappingURL=row-pluck.js.map \ No newline at end of file diff --git a/dist/recurrent/matrix/row-pluck.js.map b/dist/recurrent/matrix/row-pluck.js.map new file mode 100644 index 000000000..1eb00d69c --- /dev/null +++ b/dist/recurrent/matrix/row-pluck.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../../src/recurrent/matrix/row-pluck.js"],"names":["rowPluck","product","left","rowPluckIndex","column","columns","weights"],"mappings":";;;;;kBAKwBA,Q;AALxB;;;;;AAKe,SAASA,QAAT,CAAkBC,OAAlB,EAA2BC,IAA3B,EAAiCC,aAAjC,EAAgD;AAC7D,OAAK,IAAIC,SAAS,CAAb,EAAgBC,UAAUH,KAAKG,OAApC,EAA6CD,SAASC,OAAtD,EAA+DD,QAA/D,EAAyE;AACvEH,YAAQK,OAAR,CAAgBF,MAAhB,IAA0BF,KAAKI,OAAL,CAAaD,UAAUF,aAAV,GAA0BC,MAAvC,CAA1B;AACD;AACF","file":"row-pluck.js","sourcesContent":["/**\n * @param {Matrix} product\n * @param {Matrix} left\n * @param {Number} rowPluckIndex\n */\nexport default function rowPluck(product, left, rowPluckIndex) {\n for (let column = 0, columns = left.columns; column < columns; column++) {\n product.weights[column] = left.weights[columns * rowPluckIndex + column];\n }\n}\n"]} \ No newline at end of file diff --git a/dist/recurrent/matrix/sample-i.js b/dist/recurrent/matrix/sample-i.js new file mode 100644 index 000000000..a2afa4864 --- /dev/null +++ b/dist/recurrent/matrix/sample-i.js @@ -0,0 +1,38 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = sampleI; + +var _random = require('../../utilities/random'); + +//prevent parser from renaming when calling toString() method later +var randomF = _random.randomF; +/** + * + * @param {Matrix} m + * @returns {number} + */ +function sampleI(m) { + // sample argmax from w, assuming w are + // probabilities that sum to one + var r = randomF(0, 1); + var x = 0; + var i = 0; + var w = m.weights; + + //TODO: Needed? + if (isNaN(w[0])) { + throw new Error('NaN'); + } + + while (true) { + x += w[i]; + if (x > r) { + return i; + } + i++; + } +} +//# sourceMappingURL=sample-i.js.map \ No newline at end of file diff --git a/dist/recurrent/matrix/sample-i.js.map b/dist/recurrent/matrix/sample-i.js.map new file mode 100644 index 000000000..ffa165f97 --- /dev/null +++ b/dist/recurrent/matrix/sample-i.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../../src/recurrent/matrix/sample-i.js"],"names":["sampleI","randomF","m","r","x","i","w","weights","isNaN","Error"],"mappings":";;;;;kBASwBA,O;;AATxB;;AAEA;AACA,IAAMC,yBAAN;AACA;;;;;AAKe,SAASD,OAAT,CAAiBE,CAAjB,EAAoB;AACjC;AACA;AACA,MAAIC,IAAIF,QAAQ,CAAR,EAAW,CAAX,CAAR;AACA,MAAIG,IAAI,CAAR;AACA,MAAIC,IAAI,CAAR;AACA,MAAIC,IAAIJ,EAAEK,OAAV;;AAEA;AACA,MAAIC,MAAMF,EAAE,CAAF,CAAN,CAAJ,EAAiB;AACf,UAAM,IAAIG,KAAJ,CAAU,KAAV,CAAN;AACD;;AAED,SAAO,IAAP,EAAa;AACXL,SAAKE,EAAED,CAAF,CAAL;AACA,QAAGD,IAAID,CAAP,EAAU;AACR,aAAOE,CAAP;AACD;AACDA;AACD;AACF","file":"sample-i.js","sourcesContent":["import { randomF as _randomF } from '../../utilities/random';\n\n//prevent parser from renaming when calling toString() method later\nconst randomF = _randomF;\n/**\n *\n * @param {Matrix} m\n * @returns {number}\n */\nexport default function sampleI(m) {\n // sample argmax from w, assuming w are\n // probabilities that sum to one\n let r = randomF(0, 1);\n let x = 0;\n let i = 0;\n let w = m.weights;\n\n //TODO: Needed?\n if (isNaN(w[0])) {\n throw new Error('NaN');\n }\n\n while (true) {\n x += w[i];\n if(x > r) {\n return i;\n }\n i++;\n }\n}"]} \ No newline at end of file diff --git a/dist/recurrent/matrix/sigmoid-b.js b/dist/recurrent/matrix/sigmoid-b.js new file mode 100644 index 000000000..e2cd34fd6 --- /dev/null +++ b/dist/recurrent/matrix/sigmoid-b.js @@ -0,0 +1,18 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = sigmoidB; +/** + * + * @param {Matrix} product + * @param {Matrix} left + */ +function sigmoidB(product, left) { + for (var i = 0, max = product.recurrence.length; i < max; i++) { + var mwi = product.weights[i]; + left.recurrence[i] += mwi * (1 - mwi) * product.recurrence[i]; + } +} +//# sourceMappingURL=sigmoid-b.js.map \ No newline at end of file diff --git a/dist/recurrent/matrix/sigmoid-b.js.map b/dist/recurrent/matrix/sigmoid-b.js.map new file mode 100644 index 000000000..d2ea0b451 --- /dev/null +++ b/dist/recurrent/matrix/sigmoid-b.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../../src/recurrent/matrix/sigmoid-b.js"],"names":["sigmoidB","product","left","i","max","recurrence","length","mwi","weights"],"mappings":";;;;;kBAKwBA,Q;AALxB;;;;;AAKe,SAASA,QAAT,CAAkBC,OAAlB,EAA2BC,IAA3B,EAAiC;AAC9C,OAAI,IAAIC,IAAI,CAAR,EAAWC,MAAMH,QAAQI,UAAR,CAAmBC,MAAxC,EAAgDH,IAAIC,GAApD,EAAyDD,GAAzD,EAA8D;AAC5D,QAAII,MAAMN,QAAQO,OAAR,CAAgBL,CAAhB,CAAV;AACAD,SAAKG,UAAL,CAAgBF,CAAhB,KAAsBI,OAAO,IAAIA,GAAX,IAAkBN,QAAQI,UAAR,CAAmBF,CAAnB,CAAxC;AACD;AACF","file":"sigmoid-b.js","sourcesContent":["/**\n *\n * @param {Matrix} product\n * @param {Matrix} left\n */\nexport default function sigmoidB(product, left) {\n for(let i = 0, max = product.recurrence.length; i < max; i++) {\n let mwi = product.weights[i];\n left.recurrence[i] += mwi * (1 - mwi) * product.recurrence[i];\n }\n}\n"]} \ No newline at end of file diff --git a/dist/recurrent/matrix/sigmoid.js b/dist/recurrent/matrix/sigmoid.js new file mode 100644 index 000000000..a8d33fd9f --- /dev/null +++ b/dist/recurrent/matrix/sigmoid.js @@ -0,0 +1,22 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = sigmoid; +/** + * @param {Matrix} product + * @param {Matrix} left + */ +function sigmoid(product, left) { + // sigmoid nonlinearity + for (var i = 0, max = left.weights.length; i < max; i++) { + product.weights[i] = 1 / (1 + Math.exp(-left.weights[i])); + } +} + +function sig(x) { + // helper function for computing sigmoid + return 1 / (1 + Math.exp(-x)); +} +//# sourceMappingURL=sigmoid.js.map \ No newline at end of file diff --git a/dist/recurrent/matrix/sigmoid.js.map b/dist/recurrent/matrix/sigmoid.js.map new file mode 100644 index 000000000..5a05a8fce --- /dev/null +++ b/dist/recurrent/matrix/sigmoid.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../../src/recurrent/matrix/sigmoid.js"],"names":["sigmoid","product","left","i","max","weights","length","Math","exp","sig","x"],"mappings":";;;;;kBAIwBA,O;AAJxB;;;;AAIe,SAASA,OAAT,CAAiBC,OAAjB,EAA0BC,IAA1B,EAAgC;AAC7C;AACA,OAAI,IAAIC,IAAE,CAAN,EAASC,MAAMF,KAAKG,OAAL,CAAaC,MAAhC,EAAwCH,IAAIC,GAA5C,EAAiDD,GAAjD,EAAsD;AACpDF,YAAQI,OAAR,CAAgBF,CAAhB,IAAqB,KAAM,IAAII,KAAKC,GAAL,CAAS,CAACN,KAAKG,OAAL,CAAaF,CAAb,CAAV,CAAV,CAArB;AACD;AACF;;AAGD,SAASM,GAAT,CAAaC,CAAb,EAAgB;AACd;AACA,SAAO,KAAK,IAAIH,KAAKC,GAAL,CAAS,CAACE,CAAV,CAAT,CAAP;AACD","file":"sigmoid.js","sourcesContent":["/**\n * @param {Matrix} product\n * @param {Matrix} left\n */\nexport default function sigmoid(product, left) {\n // sigmoid nonlinearity\n for(let i=0, max = left.weights.length; i < max; i++) {\n product.weights[i] = 1 / ( 1 + Math.exp(-left.weights[i]));\n }\n}\n\n\nfunction sig(x) {\n // helper function for computing sigmoid\n return 1 / (1 + Math.exp(-x));\n}"]} \ No newline at end of file diff --git a/dist/recurrent/matrix/softmax.js b/dist/recurrent/matrix/softmax.js new file mode 100644 index 000000000..ec19b75fb --- /dev/null +++ b/dist/recurrent/matrix/softmax.js @@ -0,0 +1,48 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = softmax; + +var _ = require('./'); + +var _2 = _interopRequireDefault(_); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +//prevent parser from renaming when calling toString() method later +var Matrix = _2.default; +/** + * + * @param {Matrix} m + * @returns {Matrix} + */ +function softmax(m) { + var result = new Matrix(m.rows, m.columns); // probability volume + var maxVal = -999999; + var i = void 0; + var max = m.weights.length; + + for (i = 0; i < max; i++) { + if (m.weights[i] > maxVal) { + maxVal = m.weights[i]; + } + } + + var s = 0; + for (i = 0; i < max; i++) { + result.weights[i] = Math.exp(m.weights[i] - maxVal); + s += result.weights[i]; + } + + for (i = 0; i < max; i++) { + result.weights[i] /= s; + } + + // no backward pass here needed + // since we will use the computed probabilities outside + // to set gradients directly on m + return result; +} +//# sourceMappingURL=softmax.js.map \ No newline at end of file diff --git a/dist/recurrent/matrix/softmax.js.map b/dist/recurrent/matrix/softmax.js.map new file mode 100644 index 000000000..8fe3f75e9 --- /dev/null +++ b/dist/recurrent/matrix/softmax.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../../src/recurrent/matrix/softmax.js"],"names":["softmax","Matrix","m","result","rows","columns","maxVal","i","max","weights","length","s","Math","exp"],"mappings":";;;;;kBASwBA,O;;AATxB;;;;;;AAEA;AACA,IAAMC,mBAAN;AACA;;;;;AAKe,SAASD,OAAT,CAAiBE,CAAjB,EAAoB;AACjC,MAAIC,SAAS,IAAIF,MAAJ,CAAWC,EAAEE,IAAb,EAAmBF,EAAEG,OAArB,CAAb,CADiC,CACW;AAC5C,MAAIC,SAAS,CAAC,MAAd;AACA,MAAIC,UAAJ;AACA,MAAIC,MAAMN,EAAEO,OAAF,CAAUC,MAApB;;AAEA,OAAKH,IAAI,CAAT,EAAYA,IAAIC,GAAhB,EAAqBD,GAArB,EAA0B;AACxB,QAAGL,EAAEO,OAAF,CAAUF,CAAV,IAAeD,MAAlB,EAA0B;AACxBA,eAASJ,EAAEO,OAAF,CAAUF,CAAV,CAAT;AACD;AACF;;AAED,MAAII,IAAI,CAAR;AACA,OAAKJ,IAAI,CAAT,EAAYA,IAAIC,GAAhB,EAAqBD,GAArB,EAA0B;AACxBJ,WAAOM,OAAP,CAAeF,CAAf,IAAoBK,KAAKC,GAAL,CAASX,EAAEO,OAAF,CAAUF,CAAV,IAAeD,MAAxB,CAApB;AACAK,SAAKR,OAAOM,OAAP,CAAeF,CAAf,CAAL;AACD;;AAED,OAAKA,IAAI,CAAT,EAAYA,IAAIC,GAAhB,EAAqBD,GAArB,EAA0B;AACxBJ,WAAOM,OAAP,CAAeF,CAAf,KAAqBI,CAArB;AACD;;AAED;AACA;AACA;AACA,SAAOR,MAAP;AACD","file":"softmax.js","sourcesContent":["import _Matrix from './';\n\n//prevent parser from renaming when calling toString() method later\nconst Matrix = _Matrix;\n/**\n *\n * @param {Matrix} m\n * @returns {Matrix}\n */\nexport default function softmax(m) {\n let result = new Matrix(m.rows, m.columns); // probability volume\n let maxVal = -999999;\n let i;\n let max = m.weights.length;\n\n for (i = 0; i < max; i++) {\n if(m.weights[i] > maxVal) {\n maxVal = m.weights[i];\n }\n }\n\n let s = 0;\n for (i = 0; i < max; i++) {\n result.weights[i] = Math.exp(m.weights[i] - maxVal);\n s += result.weights[i];\n }\n\n for (i = 0; i < max; i++) {\n result.weights[i] /= s;\n }\n\n // no backward pass here needed\n // since we will use the computed probabilities outside\n // to set gradients directly on m\n return result;\n}\n"]} \ No newline at end of file diff --git a/dist/recurrent/matrix/tanh-b.js b/dist/recurrent/matrix/tanh-b.js new file mode 100644 index 000000000..6e7c6d086 --- /dev/null +++ b/dist/recurrent/matrix/tanh-b.js @@ -0,0 +1,19 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = tanhB; +/** + * + * @param {Matrix} product + * @param {Matrix} left + */ +function tanhB(product, left) { + for (var i = 0, max = product.recurrence.length; i < max; i++) { + // grad for z = tanh(x) is (1 - z^2) + var mwi = product.weights[i]; + left.recurrence[i] += (1 - mwi * mwi) * product.recurrence[i]; + } +} +//# sourceMappingURL=tanh-b.js.map \ No newline at end of file diff --git a/dist/recurrent/matrix/tanh-b.js.map b/dist/recurrent/matrix/tanh-b.js.map new file mode 100644 index 000000000..260cd8928 --- /dev/null +++ b/dist/recurrent/matrix/tanh-b.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../../src/recurrent/matrix/tanh-b.js"],"names":["tanhB","product","left","i","max","recurrence","length","mwi","weights"],"mappings":";;;;;kBAKwBA,K;AALxB;;;;;AAKe,SAASA,KAAT,CAAeC,OAAf,EAAwBC,IAAxB,EAA8B;AAC3C,OAAI,IAAIC,IAAI,CAAR,EAAWC,MAAMH,QAAQI,UAAR,CAAmBC,MAAxC,EAAgDH,IAAIC,GAApD,EAAyDD,GAAzD,EAA8D;AAC5D;AACA,QAAII,MAAMN,QAAQO,OAAR,CAAgBL,CAAhB,CAAV;AACAD,SAAKG,UAAL,CAAgBF,CAAhB,KAAsB,CAAC,IAAII,MAAMA,GAAX,IAAkBN,QAAQI,UAAR,CAAmBF,CAAnB,CAAxC;AACD;AACF","file":"tanh-b.js","sourcesContent":["/**\n *\n * @param {Matrix} product\n * @param {Matrix} left\n */\nexport default function tanhB(product, left) {\n for(let i = 0, max = product.recurrence.length; i < max; i++) {\n // grad for z = tanh(x) is (1 - z^2)\n let mwi = product.weights[i];\n left.recurrence[i] += (1 - mwi * mwi) * product.recurrence[i];\n }\n}\n"]} \ No newline at end of file diff --git a/dist/recurrent/matrix/tanh.js b/dist/recurrent/matrix/tanh.js new file mode 100644 index 000000000..9af9266c7 --- /dev/null +++ b/dist/recurrent/matrix/tanh.js @@ -0,0 +1,17 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = tanh; +/** + * @param {Matrix} product + * @param {Matrix} left + */ +function tanh(product, left) { + // tanh nonlinearity + for (var i = 0, max = left.weights.length; i < max; i++) { + product.weights[i] = Math.tanh(left.weights[i]); + } +} +//# sourceMappingURL=tanh.js.map \ No newline at end of file diff --git a/dist/recurrent/matrix/tanh.js.map b/dist/recurrent/matrix/tanh.js.map new file mode 100644 index 000000000..de347409c --- /dev/null +++ b/dist/recurrent/matrix/tanh.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../../src/recurrent/matrix/tanh.js"],"names":["tanh","product","left","i","max","weights","length","Math"],"mappings":";;;;;kBAIwBA,I;AAJxB;;;;AAIe,SAASA,IAAT,CAAcC,OAAd,EAAuBC,IAAvB,EAA6B;AAC1C;AACA,OAAI,IAAIC,IAAI,CAAR,EAAWC,MAAMF,KAAKG,OAAL,CAAaC,MAAlC,EAA0CH,IAAIC,GAA9C,EAAmDD,GAAnD,EAAwD;AACtDF,YAAQI,OAAR,CAAgBF,CAAhB,IAAqBI,KAAKP,IAAL,CAAUE,KAAKG,OAAL,CAAaF,CAAb,CAAV,CAArB;AACD;AACF","file":"tanh.js","sourcesContent":["/**\n * @param {Matrix} product\n * @param {Matrix} left\n */\nexport default function tanh(product, left) {\n // tanh nonlinearity\n for(let i = 0, max = left.weights.length; i < max; i++) {\n product.weights[i] = Math.tanh(left.weights[i]);\n }\n}\n"]} \ No newline at end of file diff --git a/dist/recurrent/rnn.js b/dist/recurrent/rnn.js new file mode 100644 index 000000000..d7351b2c2 --- /dev/null +++ b/dist/recurrent/rnn.js @@ -0,0 +1,702 @@ +'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; }; }(); + +var _matrix = require('./matrix'); + +var _matrix2 = _interopRequireDefault(_matrix); + +var _randomMatrix = require('./matrix/random-matrix'); + +var _randomMatrix2 = _interopRequireDefault(_randomMatrix); + +var _equation = require('./matrix/equation'); + +var _equation2 = _interopRequireDefault(_equation); + +var _sampleI2 = require('./matrix/sample-i'); + +var _sampleI3 = _interopRequireDefault(_sampleI2); + +var _maxI = require('./matrix/max-i'); + +var _maxI2 = _interopRequireDefault(_maxI); + +var _softmax = require('./matrix/softmax'); + +var _softmax2 = _interopRequireDefault(_softmax); + +var _copy = require('./matrix/copy'); + +var _copy2 = _interopRequireDefault(_copy); + +var _random = require('../utilities/random'); + +var _zeros = require('../utilities/zeros'); + +var _zeros2 = _interopRequireDefault(_zeros); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var defaults = { + isBackPropagate: true, + // hidden size should be a list + inputSize: 20, + inputRange: 20, + hiddenSizes: [20, 20], + outputSize: 20, + learningRate: 0.01, + decayRate: 0.999, + smoothEps: 1e-8, + regc: 0.000001, + clipval: 5, + json: null +}; + +var RNN = function () { + function RNN(options) { + _classCallCheck(this, RNN); + + options = options || {}; + + for (var p in defaults) { + if (defaults.hasOwnProperty(p) && p !== 'isBackPropagate') { + this[p] = options.hasOwnProperty(p) ? options[p] : defaults[p]; + } + } + + this.stepCache = {}; + this.runs = 0; + this.totalPerplexity = null; + this.totalCost = null; + this.ratioClipped = null; + + this.model = { + input: null, + hiddenLayers: [], + output: null, + equations: [], + allMatrices: [], + outputMatrixIndex: -1, + equationConnections: [] + }; + + if (this.json) { + this.fromJSON(this.json); + } else { + this.mapModel(); + } + } + + _createClass(RNN, [{ + key: 'createHiddenLayers', + value: function createHiddenLayers() { + var hiddenSizes = this.hiddenSizes; + var model = this.model; + var hiddenLayers = model.hiddenLayers; + //0 is end, so add 1 to offset + hiddenLayers.push(this.getModel(hiddenSizes[0], this.inputSize)); + var prevSize = hiddenSizes[0]; + + for (var d = 1; d < hiddenSizes.length; d++) { + // loop over depths + var hiddenSize = hiddenSizes[d]; + hiddenLayers.push(this.getModel(hiddenSize, prevSize)); + prevSize = hiddenSize; + } + } + + /** + * + * @param {Number} hiddenSize + * @param {Number} prevSize + * @returns {object} + */ + + }, { + key: 'getModel', + value: function getModel(hiddenSize, prevSize) { + return { + //wxh + weight: new _randomMatrix2.default(hiddenSize, prevSize, 0.08), + //whh + transition: new _randomMatrix2.default(hiddenSize, hiddenSize, 0.08), + //bhh + bias: new _matrix2.default(hiddenSize, 1) + }; + } + + /** + * + * @param {Equation} equation + * @param {Matrix} inputMatrix + * @param {Matrix} previousResult + * @param {Object} hiddenLayer + * @returns {Matrix} + */ + + }, { + key: 'getEquation', + value: function getEquation(equation, inputMatrix, previousResult, hiddenLayer) { + var relu = equation.relu.bind(equation); + var add = equation.add.bind(equation); + var multiply = equation.multiply.bind(equation); + + return relu(add(add(multiply(hiddenLayer.weight, inputMatrix), multiply(hiddenLayer.transition, previousResult)), hiddenLayer.bias)); + } + }, { + key: 'createInputMatrix', + value: function createInputMatrix() { + //0 is end, so add 1 to offset + this.model.input = new _randomMatrix2.default(this.inputRange + 1, this.inputSize, 0.08); + } + }, { + key: 'createOutputMatrix', + value: function createOutputMatrix() { + var model = this.model; + var outputSize = this.outputSize; + var lastHiddenSize = this.hiddenSizes[this.hiddenSizes.length - 1]; + + //0 is end, so add 1 to offset + //whd + model.outputConnector = new _randomMatrix2.default(outputSize + 1, lastHiddenSize, 0.08); + //0 is end, so add 1 to offset + //bd + model.output = new _matrix2.default(outputSize + 1, 1); + } + }, { + key: 'bindEquation', + value: function bindEquation() { + var model = this.model; + var hiddenSizes = this.hiddenSizes; + var hiddenLayers = model.hiddenLayers; + var equation = new _equation2.default(); + var outputs = []; + var equationConnection = model.equationConnections.length > 0 ? model.equationConnections[model.equationConnections.length - 1] : hiddenSizes.map(function (size) { + return new _matrix2.default(hiddenSizes[0], 1); + }); + + // 0 index + var output = this.getEquation(equation, equation.inputMatrixToRow(model.input), equationConnection[0], hiddenLayers[0]); + outputs.push(output); + // 1+ indexes + for (var i = 1, max = hiddenSizes.length; i < max; i++) { + output = this.getEquation(equation, output, equationConnection[i], hiddenLayers[i]); + outputs.push(output); + } + + model.equationConnections.push(outputs); + equation.add(equation.multiply(model.outputConnector, output), model.output); + model.allMatrices = model.allMatrices.concat(equation.allMatrices); + model.equations.push(equation); + } + }, { + key: 'mapModel', + value: function mapModel() { + var model = this.model; + var hiddenLayers = model.hiddenLayers; + var allMatrices = model.allMatrices; + + this.createInputMatrix(); + if (!model.input) throw new Error('net.model.input not set'); + allMatrices.push(model.input); + + this.createHiddenLayers(); + if (!model.hiddenLayers.length) throw new Error('net.hiddenLayers not set'); + for (var i = 0, max = hiddenLayers.length; i < max; i++) { + var hiddenMatrix = hiddenLayers[i]; + for (var property in hiddenMatrix) { + if (!hiddenMatrix.hasOwnProperty(property)) continue; + allMatrices.push(hiddenMatrix[property]); + } + } + + this.createOutputMatrix(); + if (!model.outputConnector) throw new Error('net.model.outputConnector not set'); + if (!model.output) throw new Error('net.model.output not set'); + + allMatrices.push(model.outputConnector); + model.outputMatrixIndex = allMatrices.length; + allMatrices.push(model.output); + } + }, { + key: 'run', + value: function run(input) { + this.train(input); + this.runBackpropagate(input); + this.step(); + } + }, { + key: 'train', + value: function train(input) { + this.runs++; + var model = this.model; + var max = input.length; + var log2ppl = 0; + var cost = 0; + + var equation = void 0; + while (model.equations.length <= input.length + 1) { + //first and last are zeros + this.bindEquation(); + } + for (var inputIndex = -1, inputMax = input.length; inputIndex < inputMax; inputIndex++) { + // start and end tokens are zeros + equation = model.equations[inputIndex + 1]; + + var source = inputIndex === -1 ? 0 : input[inputIndex] + 1; // first step: start with START token + var target = inputIndex === max - 1 ? 0 : input[inputIndex + 1] + 1; // last step: end with END token + var output = equation.run(source); + // set gradients into log probabilities + var logProbabilities = output; // interpret output as log probabilities + var probabilities = (0, _softmax2.default)(output); // compute the softmax probabilities + + log2ppl += -Math.log2(probabilities.weights[target]); // accumulate base 2 log prob and do smoothing + cost += -Math.log(probabilities.weights[target]); + + // write gradients into log probabilities + logProbabilities.recurrence = probabilities.weights; + logProbabilities.recurrence[target] -= 1; + } + + this.totalPerplexity = Math.pow(2, log2ppl / (max - 1)); + this.totalCost = cost; + } + }, { + key: 'runBackpropagate', + value: function runBackpropagate(input) { + var i = input.length + 0; + var model = this.model; + var equations = model.equations; + while (i > 0) { + equations[i].runBackpropagate(input[i - 1] + 1); + i--; + } + equations[0].runBackpropagate(0); + } + }, { + key: 'step', + value: function step() { + // perform parameter update + var stepSize = this.learningRate; + var regc = this.regc; + var clipval = this.clipval; + var model = this.model; + var numClipped = 0; + var numTot = 0; + var allMatrices = model.allMatrices; + var matrixIndexes = allMatrices.length; + for (var matrixIndex = 0; matrixIndex < matrixIndexes; matrixIndex++) { + var matrix = allMatrices[matrixIndex]; + if (!(matrixIndex in this.stepCache)) { + this.stepCache[matrixIndex] = new _matrix2.default(matrix.rows, matrix.columns); + } + var cache = this.stepCache[matrixIndex]; + + //if we are in an equation, reset the weights and recurrence to 0, to prevent exploding gradient problem + if (matrixIndex > model.outputMatrixIndex) { + for (var i = 0, n = matrix.weights.length; i < n; i++) { + matrix.weights[i] = 0; + matrix.recurrence[i] = 0; + } + continue; + } + + for (var _i = 0, _n = matrix.weights.length; _i < _n; _i++) { + // rmsprop adaptive learning rate + var mdwi = matrix.recurrence[_i]; + cache.weights[_i] = cache.weights[_i] * this.decayRate + (1 - this.decayRate) * mdwi * mdwi; + // gradient clip + if (mdwi > clipval) { + mdwi = clipval; + numClipped++; + } + if (mdwi < -clipval) { + mdwi = -clipval; + numClipped++; + } + numTot++; + + // update (and regularize) + matrix.weights[_i] = matrix.weights[_i] + -stepSize * mdwi / Math.sqrt(cache.weights[_i] + this.smoothEps) - regc * matrix.weights[_i]; + matrix.recurrence[_i] = 0; // reset gradients for next iteration + } + } + this.ratioClipped = numClipped / numTot; + } + }, { + key: 'predict', + value: function predict() { + var result = arguments.length <= 0 || arguments[0] === undefined ? [] : arguments[0]; + var maxPredictionLength = arguments.length <= 1 || arguments[1] === undefined ? 100 : arguments[1]; + + var _sampleI = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2]; + + var temperature = arguments.length <= 3 || arguments[3] === undefined ? 1 : arguments[3]; + + var model = this.model; + var equation = void 0; + var i = 0; + while (model.equations.length < maxPredictionLength) { + this.bindEquation(); + } + while (true) { + var ix = result.length === 0 ? 0 : result[result.length - 1]; + equation = model.equations[i]; + // sample predicted letter + var output = equation.run(ix); + + var logProbabilities = new _matrix2.default(model.output.rows, model.output.columns); + (0, _copy2.default)(logProbabilities, output); + if (temperature !== 1 && _sampleI) { + // scale log probabilities by temperature and renormalize + // if temperature is high, logprobs will go towards zero + // and the softmax outputs will be more diffuse. if temperature is + // very low, the softmax outputs will be more peaky + for (var q = 0, nq = logProbabilities.weights.length; q < nq; q++) { + logProbabilities.weights[q] /= temperature; + } + } + + var probs = (0, _softmax2.default)(logProbabilities); + + if (_sampleI) { + ix = (0, _sampleI3.default)(probs); + } else { + ix = (0, _maxI2.default)(probs); + } + + i++; + if (ix === 0) { + // END token predicted, break out + break; + } + if (i >= maxPredictionLength) { + // something is wrong + break; + } + + result.push(ix); + } + + return result.map(function (value) { + return value - 1; + }); + } + + /** + * + * @param input + * @returns {*} + */ + + }, { + key: 'runInput', + value: function runInput(input) { + this.outputs[0] = input; // set output state of input layer + + var output = null; + for (var layer = 1; layer <= this.outputLayer; layer++) { + for (var node = 0; node < this.sizes[layer]; node++) { + var weights = this.weights[layer][node]; + + var sum = this.biases[layer][node]; + for (var k = 0; k < weights.length; k++) { + sum += weights[k] * input[k]; + } + this.outputs[layer][node] = 1 / (1 + Math.exp(-sum)); + } + output = input = this.outputs[layer]; + } + return output; + } + + /** + * + * @param data + * @param options + * @returns {{error: number, iterations: number}} + */ + /*train(data, options) { + throw new Error('not yet implemented'); + //data = this.formatData(data); + options = options || {}; + let iterations = options.iterations || 20000; + let errorThresh = options.errorThresh || 0.005; + let log = options.log ? (typeof options.log === 'function' ? options.log : console.log) : false; + let logPeriod = options.logPeriod || 10; + let learningRate = options.learningRate || this.learningRate || 0.3; + let callback = options.callback; + let callbackPeriod = options.callbackPeriod || 10; + let sizes = []; + let inputSize = data[0].input.length; + let outputSize = data[0].output.length; + let hiddenSizes = this.hiddenSizes; + if (!hiddenSizes) { + sizes.push(Math.max(3, Math.floor(inputSize / 2))); + } else { + hiddenSizes.forEach(function(size) { + sizes.push(size); + }); + } + sizes.unshift(inputSize); + sizes.push(outputSize); + //this.initialize(sizes, options.keepNetworkIntact); + let error = 1; + for (let i = 0; i < iterations && error > errorThresh; i++) { + let sum = 0; + for (let j = 0; j < data.length; j++) { + let err = this.trainPattern(data[j].input, data[j].output, learningRate); + sum += err; + } + error = sum / data.length; + if (log && (i % logPeriod == 0)) { + log('iterations:', i, 'training error:', error); + } + if (callback && (i % callbackPeriod == 0)) { + callback({ error: error, iterations: i }); + } + } + return { + error: error, + iterations: i + }; + }*/ + + /** + * + * @param input + * @param target + * @param learningRate + */ + + }, { + key: 'trainPattern', + value: function trainPattern(input, target, learningRate) { + throw new Error('not yet implemented'); + } + + /** + * + * @param target + */ + + }, { + key: 'calculateDeltas', + value: function calculateDeltas(target) { + throw new Error('not yet implemented'); + } + + /** + * + * @param learningRate + */ + + }, { + key: 'adjustWeights', + value: function adjustWeights(learningRate) { + throw new Error('not yet implemented'); + } + + /** + * + * @param data + * @returns {*} + */ + + }, { + key: 'formatData', + value: function formatData(data) { + throw new Error('not yet implemented'); + } + + /** + * + * @param data + * @returns { + * { + * error: number, + * misclasses: Array + * } + * } + */ + + }, { + key: 'test', + value: function test(data) { + throw new Error('not yet implemented'); + } + }, { + key: 'toJSON', + value: function toJSON() { + var model = this.model; + var options = {}; + for (var p in defaults) { + options[p] = this[p]; + } + + return { + type: this.constructor.name, + options: options, + input: model.input.toJSON(), + hiddenLayers: model.hiddenLayers.map(function (hiddenLayer) { + var layers = {}; + for (var _p in hiddenLayer) { + layers[_p] = hiddenLayer[_p].toJSON(); + } + return layers; + }), + outputConnector: this.model.outputConnector.toJSON(), + output: this.model.output.toJSON() + }; + } + }, { + key: 'fromJSON', + value: function fromJSON(json) { + this.json = json; + var model = this.model; + var options = json.options; + var allMatrices = model.allMatrices; + model.input = _matrix2.default.fromJSON(json.input); + allMatrices.push(model.input); + model.hiddenLayers = json.hiddenLayers.map(function (hiddenLayer) { + var layers = {}; + for (var p in hiddenLayer) { + layers[p] = _matrix2.default.fromJSON(hiddenLayer[p]); + allMatrices.push(layers[p]); + } + return layers; + }); + model.outputConnector = _matrix2.default.fromJSON(json.outputConnector); + model.output = _matrix2.default.fromJSON(json.output); + allMatrices.push(model.outputConnector, model.output); + + for (var p in defaults) { + if (defaults.hasOwnProperty(p) && p !== 'isBackPropagate') { + this[p] = options.hasOwnProperty(p) ? options[p] : defaults[p]; + } + } + + this.bindEquation(); + } + + /** + * + * @returns {Function} + */ + + }, { + key: 'toFunction', + value: function toFunction() { + var model = this.model; + var equations = this.model.equations; + var equation = equations[1]; + var states = equation.states; + var modelAsString = JSON.stringify(this.toJSON()); + + function matrixOrigin(m, stateIndex) { + for (var i = 0, max = states.length; i < max; i++) { + var state = states[i]; + + if (i === stateIndex) { + var j = previousConnectionIndex(m); + switch (m) { + case state.left: + if (j > -1) { + return 'typeof prevStates[' + j + '] === \'object\' ? prevStates[' + j + '].product : new Matrix(' + m.rows + ', ' + m.columns + ')'; + } + case state.right: + if (j > -1) { + return 'typeof prevStates[' + j + '] === \'object\' ? prevStates[' + j + '].product : new Matrix(' + m.rows + ', ' + m.columns + ')'; + } + case state.product: + return 'new Matrix(' + m.rows + ', ' + m.columns + ')'; + default: + throw Error('unknown state'); + } + } + + if (m === state.product) return 'states[' + i + '].product'; + if (m === state.right) return 'states[' + i + '].right'; + if (m === state.left) return 'states[' + i + '].left'; + } + } + + function previousConnectionIndex(m) { + var connection = model.equationConnections[0]; + var states = equations[0].states; + for (var i = 0, max = states.length; i < max; i++) { + if (states[i].product === m) { + return i; + } + } + return connection.indexOf(m); + } + + function matrixToString(m, stateIndex) { + if (!m || !m.rows || !m.columns) return 'null'; + + if (m === model.input) return 'model.input'; + if (m === model.outputConnector) return 'model.outputConnector'; + if (m === model.output) return 'model.output'; + + for (var i = 0, max = model.hiddenLayers.length; i < max; i++) { + var hiddenLayer = model.hiddenLayers[i]; + for (var p in hiddenLayer) { + if (!hiddenLayer.hasOwnProperty(p)) continue; + if (hiddenLayer[p] !== m) continue; + return 'model.hiddenLayers[' + i + '].' + p; + } + } + + return matrixOrigin(m, stateIndex); + } + + function toInner(fnString) { + //crude, but should be sufficient for now + // function() { body } + fnString = fnString.toString().split('{'); + fnString.shift(); + // body } + fnString = fnString.join('{'); + fnString = fnString.split('}'); + fnString.pop(); + // body + return fnString.join('}').split('\n').join('\n '); + } + + function fileName(fnName) { + return 'src/recurrent/matrix/' + fnName.replace(/[A-Z]/g, function (value) { + return '-' + value.toLowerCase(); + }) + '.js'; + } + + var statesRaw = []; + var usedFunctionNames = {}; + var innerFunctionsSwitch = []; + for (var i = 0, max = states.length; i < max; i++) { + var state = states[i]; + statesRaw.push('states[' + i + '] = {\n name: \'' + state.forwardFn.name + '\',\n left: ' + matrixToString(state.left, i) + ',\n right: ' + matrixToString(state.right, i) + ',\n product: ' + matrixToString(state.product, i) + '\n }'); + + var fnName = state.forwardFn.name; + if (!usedFunctionNames[fnName]) { + usedFunctionNames[fnName] = true; + innerFunctionsSwitch.push(' case \'' + fnName + '\': //compiled from ' + fileName(fnName) + '\n ' + toInner(state.forwardFn.toString()) + '\n break;'); + } + } + + return new Function('input', 'maxPredictionLength', '_sampleI', 'temperature', '\n if (typeof input === \'undefined\') input = [];\n if (typeof maxPredictionLength === \'undefined\') maxPredictionLength = 100;\n if (typeof _sampleI === \'undefined\') _sampleI = false;\n if (typeof temperature === \'undefined\') temperature = 1;\n \n var model = ' + modelAsString + ';\n var _i = 0;\n var result = input.slice(0);\n var states = [];\n var prevStates;\n while (true) {\n // sample predicted letter\n var ix = result.length === 0 ? 0 : result[result.length - 1]; // first step: start with START token\n var rowPluckIndex = ix; //connect up to rowPluck\n prevStates = states;\n states = [];\n ' + statesRaw.join(';\n ') + ';\n for (var stateIndex = 0, stateMax = ' + statesRaw.length + '; stateIndex < stateMax; stateIndex++) {\n var state = states[stateIndex];\n var product = state.product;\n var left = state.left;\n var right = state.right;\n \n switch (state.name) {\n' + innerFunctionsSwitch.join('\n') + '\n }\n }\n \n var logProbabilities = state.product;\n if (temperature !== 1 && _sampleI) {\n // scale log probabilities by temperature and renormalize\n // if temperature is high, logprobs will go towards zero\n // and the softmax outputs will be more diffuse. if temperature is\n // very low, the softmax outputs will be more peaky\n for (var q = 0, nq = logProbabilities.weights.length; q < nq; q++) {\n logProbabilities.weights[q] /= temperature;\n }\n }\n\n var probs = softmax(logProbabilities);\n\n if (_sampleI) {\n ix = sampleI(probs);\n } else {\n ix = maxI(probs);\n }\n \n _i++;\n if (ix === 0) {\n // END token predicted, break out\n break;\n }\n if (_i >= maxPredictionLength) {\n // something is wrong\n break;\n }\n\n result.push(ix);\n }\n\n return result.map(function(value) { return value - 1; });\n \n function Matrix(rows, columns) {\n this.rows = rows;\n this.columns = columns;\n this.weights = zeros(rows * columns);\n this.recurrence = zeros(rows * columns);\n }\n ' + _zeros2.default.toString() + '\n ' + _softmax2.default.toString() + '\n ' + _random.randomF.toString() + '\n ' + _sampleI3.default.toString() + '\n ' + _maxI2.default.toString()); + } + }]); + + return RNN; +}(); + +exports.default = RNN; +//# sourceMappingURL=rnn.js.map \ No newline at end of file diff --git a/dist/recurrent/rnn.js.map b/dist/recurrent/rnn.js.map new file mode 100644 index 000000000..ee98ba79c --- /dev/null +++ b/dist/recurrent/rnn.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../src/recurrent/rnn.js"],"names":["defaults","isBackPropagate","inputSize","inputRange","hiddenSizes","outputSize","learningRate","decayRate","smoothEps","regc","clipval","json","RNN","options","p","hasOwnProperty","stepCache","runs","totalPerplexity","totalCost","ratioClipped","model","input","hiddenLayers","output","equations","allMatrices","outputMatrixIndex","equationConnections","fromJSON","mapModel","push","getModel","prevSize","d","length","hiddenSize","weight","transition","bias","equation","inputMatrix","previousResult","hiddenLayer","relu","bind","add","multiply","lastHiddenSize","outputConnector","outputs","equationConnection","map","size","getEquation","inputMatrixToRow","i","max","concat","createInputMatrix","Error","createHiddenLayers","hiddenMatrix","property","createOutputMatrix","train","runBackpropagate","step","log2ppl","cost","bindEquation","inputIndex","inputMax","source","target","run","logProbabilities","probabilities","Math","log2","weights","log","recurrence","pow","stepSize","numClipped","numTot","matrixIndexes","matrixIndex","matrix","rows","columns","cache","n","mdwi","sqrt","result","maxPredictionLength","_sampleI","temperature","ix","q","nq","probs","value","layer","outputLayer","node","sizes","sum","biases","k","exp","data","type","constructor","name","toJSON","layers","states","modelAsString","JSON","stringify","matrixOrigin","m","stateIndex","state","j","previousConnectionIndex","left","right","product","connection","indexOf","matrixToString","toInner","fnString","toString","split","shift","join","pop","fileName","fnName","replace","toLowerCase","statesRaw","usedFunctionNames","innerFunctionsSwitch","forwardFn","Function"],"mappings":"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","file":"rnn.js","sourcesContent":["import Matrix from './matrix';\nimport RandomMatrix from './matrix/random-matrix';\nimport Equation from './matrix/equation';\nimport sampleI from './matrix/sample-i';\nimport maxI from './matrix/max-i';\nimport softmax from './matrix/softmax';\nimport copy from './matrix/copy';\nimport { randomF } from '../utilities/random';\nimport zeros from '../utilities/zeros';\n\nconst defaults = {\n isBackPropagate: true,\n // hidden size should be a list\n inputSize: 20,\n inputRange: 20,\n hiddenSizes:[20,20],\n outputSize: 20,\n learningRate: 0.01,\n decayRate: 0.999,\n smoothEps: 1e-8,\n regc: 0.000001,\n clipval: 5,\n json: null\n};\n\nexport default class RNN {\n constructor(options) {\n options = options || {};\n\n for (let p in defaults) {\n if (defaults.hasOwnProperty(p) && p !== 'isBackPropagate') {\n this[p] = options.hasOwnProperty(p) ? options[p] : defaults[p];\n }\n }\n\n this.stepCache = {};\n this.runs = 0;\n this.totalPerplexity = null;\n this.totalCost = null;\n this.ratioClipped = null;\n\n this.model = {\n input: null,\n hiddenLayers: [],\n output: null,\n equations: [],\n allMatrices: [],\n outputMatrixIndex: -1,\n equationConnections: []\n };\n\n if (this.json) {\n this.fromJSON(this.json);\n } else {\n this.mapModel();\n }\n }\n\n createHiddenLayers() {\n let hiddenSizes = this.hiddenSizes;\n let model = this.model;\n let hiddenLayers = model.hiddenLayers;\n //0 is end, so add 1 to offset\n hiddenLayers.push(this.getModel(hiddenSizes[0], this.inputSize));\n let prevSize = hiddenSizes[0];\n\n for (let d = 1; d < hiddenSizes.length; d++) { // loop over depths\n let hiddenSize = hiddenSizes[d];\n hiddenLayers.push(this.getModel(hiddenSize, prevSize));\n prevSize = hiddenSize;\n }\n }\n\n /**\n *\n * @param {Number} hiddenSize\n * @param {Number} prevSize\n * @returns {object}\n */\n getModel(hiddenSize, prevSize) {\n return {\n //wxh\n weight: new RandomMatrix(hiddenSize, prevSize, 0.08),\n //whh\n transition: new RandomMatrix(hiddenSize, hiddenSize, 0.08),\n //bhh\n bias: new Matrix(hiddenSize, 1)\n };\n }\n\n /**\n *\n * @param {Equation} equation\n * @param {Matrix} inputMatrix\n * @param {Matrix} previousResult\n * @param {Object} hiddenLayer\n * @returns {Matrix}\n */\n getEquation(equation, inputMatrix, previousResult, hiddenLayer) {\n let relu = equation.relu.bind(equation);\n let add = equation.add.bind(equation);\n let multiply = equation.multiply.bind(equation);\n\n return relu(\n add(\n add(\n multiply(\n hiddenLayer.weight,\n inputMatrix\n ),\n multiply(\n hiddenLayer.transition,\n previousResult\n )\n ),\n hiddenLayer.bias\n )\n );\n }\n\n createInputMatrix() {\n //0 is end, so add 1 to offset\n this.model.input = new RandomMatrix(this.inputRange + 1, this.inputSize, 0.08);\n }\n\n createOutputMatrix() {\n let model = this.model;\n let outputSize = this.outputSize;\n let lastHiddenSize = this.hiddenSizes[this.hiddenSizes.length - 1];\n\n //0 is end, so add 1 to offset\n //whd\n model.outputConnector = new RandomMatrix(outputSize + 1, lastHiddenSize, 0.08);\n //0 is end, so add 1 to offset\n //bd\n model.output = new Matrix(outputSize + 1, 1);\n }\n\n bindEquation() {\n let model = this.model;\n let hiddenSizes = this.hiddenSizes;\n let hiddenLayers = model.hiddenLayers;\n let equation = new Equation();\n let outputs = [];\n let equationConnection = model.equationConnections.length > 0\n ? model.equationConnections[model.equationConnections.length - 1]\n : hiddenSizes.map((size) => new Matrix(hiddenSizes[0], 1))\n ;\n\n // 0 index\n let output = this.getEquation(equation, equation.inputMatrixToRow(model.input), equationConnection[0], hiddenLayers[0]);\n outputs.push(output);\n // 1+ indexes\n for (let i = 1, max = hiddenSizes.length; i < max; i++) {\n output = this.getEquation(equation, output, equationConnection[i], hiddenLayers[i]);\n outputs.push(output);\n }\n\n model.equationConnections.push(outputs);\n equation.add(equation.multiply(model.outputConnector, output), model.output);\n model.allMatrices = model.allMatrices.concat(equation.allMatrices);\n model.equations.push(equation);\n }\n\n mapModel() {\n let model = this.model;\n let hiddenLayers = model.hiddenLayers;\n let allMatrices = model.allMatrices;\n\n this.createInputMatrix();\n if (!model.input) throw new Error('net.model.input not set');\n allMatrices.push(model.input);\n\n this.createHiddenLayers();\n if (!model.hiddenLayers.length) throw new Error('net.hiddenLayers not set');\n for (let i = 0, max = hiddenLayers.length; i < max; i++) {\n let hiddenMatrix = hiddenLayers[i];\n for (let property in hiddenMatrix) {\n if (!hiddenMatrix.hasOwnProperty(property)) continue;\n allMatrices.push(hiddenMatrix[property]);\n }\n }\n\n this.createOutputMatrix();\n if (!model.outputConnector) throw new Error('net.model.outputConnector not set');\n if (!model.output) throw new Error('net.model.output not set');\n\n allMatrices.push(model.outputConnector);\n model.outputMatrixIndex = allMatrices.length;\n allMatrices.push(model.output);\n }\n\n run(input) {\n this.train(input);\n this.runBackpropagate(input);\n this.step();\n }\n\n train(input) {\n this.runs++;\n let model = this.model;\n let max = input.length;\n let log2ppl = 0;\n let cost = 0;\n\n let equation;\n while (model.equations.length <= input.length + 1) {//first and last are zeros\n this.bindEquation();\n }\n for (let inputIndex = -1, inputMax = input.length; inputIndex < inputMax; inputIndex++) {\n // start and end tokens are zeros\n equation = model.equations[inputIndex + 1];\n\n let source = (inputIndex === -1 ? 0 : input[inputIndex] + 1); // first step: start with START token\n let target = (inputIndex === max - 1 ? 0 : input[inputIndex + 1] + 1); // last step: end with END token\n let output = equation.run(source);\n // set gradients into log probabilities\n let logProbabilities = output; // interpret output as log probabilities\n let probabilities = softmax(output); // compute the softmax probabilities\n\n log2ppl += -Math.log2(probabilities.weights[target]); // accumulate base 2 log prob and do smoothing\n cost += -Math.log(probabilities.weights[target]);\n\n // write gradients into log probabilities\n logProbabilities.recurrence = probabilities.weights;\n logProbabilities.recurrence[target] -= 1;\n }\n\n this.totalPerplexity = Math.pow(2, log2ppl / (max - 1));\n this.totalCost = cost;\n }\n\n runBackpropagate(input) {\n var i = input.length + 0;\n var model = this.model;\n var equations = model.equations;\n while(i > 0) {\n equations[i].runBackpropagate(input[i - 1] + 1);\n i--;\n }\n equations[0].runBackpropagate(0);\n }\n\n step() {\n // perform parameter update\n let stepSize = this.learningRate;\n let regc = this.regc;\n let clipval = this.clipval;\n let model = this.model;\n let numClipped = 0;\n let numTot = 0;\n let allMatrices = model.allMatrices;\n let matrixIndexes = allMatrices.length;\n for (let matrixIndex = 0; matrixIndex < matrixIndexes; matrixIndex++) {\n let matrix = allMatrices[matrixIndex];\n if (!(matrixIndex in this.stepCache)) {\n this.stepCache[matrixIndex] = new Matrix(matrix.rows, matrix.columns);\n }\n let cache = this.stepCache[matrixIndex];\n\n //if we are in an equation, reset the weights and recurrence to 0, to prevent exploding gradient problem\n if (matrixIndex > model.outputMatrixIndex) {\n for (let i = 0, n = matrix.weights.length; i < n; i++) {\n matrix.weights[i] = 0;\n matrix.recurrence[i] = 0;\n }\n continue;\n }\n\n for (let i = 0, n = matrix.weights.length; i < n; i++) {\n // rmsprop adaptive learning rate\n let mdwi = matrix.recurrence[i];\n cache.weights[i] = cache.weights[i] * this.decayRate + (1 - this.decayRate) * mdwi * mdwi;\n // gradient clip\n if (mdwi > clipval) {\n mdwi = clipval;\n numClipped++;\n }\n if (mdwi < -clipval) {\n mdwi = -clipval;\n numClipped++;\n }\n numTot++;\n\n // update (and regularize)\n matrix.weights[i] = matrix.weights[i] + -stepSize * mdwi / Math.sqrt(cache.weights[i] + this.smoothEps) - regc * matrix.weights[i];\n matrix.recurrence[i] = 0; // reset gradients for next iteration\n }\n }\n this.ratioClipped = numClipped / numTot;\n }\n\n predict(result = [], maxPredictionLength = 100, _sampleI = false, temperature = 1) {\n let model = this.model;\n let equation;\n let i = 0;\n while (model.equations.length < maxPredictionLength) {\n this.bindEquation();\n }\n while (true) {\n let ix = result.length === 0 ? 0 : result[result.length - 1];\n equation = model.equations[i];\n // sample predicted letter\n let output = equation.run(ix);\n\n let logProbabilities = new Matrix(model.output.rows, model.output.columns);\n copy(logProbabilities, output);\n if (temperature !== 1 && _sampleI) {\n // scale log probabilities by temperature and renormalize\n // if temperature is high, logprobs will go towards zero\n // and the softmax outputs will be more diffuse. if temperature is\n // very low, the softmax outputs will be more peaky\n for (let q = 0, nq = logProbabilities.weights.length; q < nq; q++) {\n logProbabilities.weights[q] /= temperature;\n }\n }\n\n let probs = softmax(logProbabilities);\n\n if (_sampleI) {\n ix = sampleI(probs);\n } else {\n ix = maxI(probs);\n }\n\n i++;\n if (ix === 0) {\n // END token predicted, break out\n break;\n }\n if (i >= maxPredictionLength) {\n // something is wrong\n break;\n }\n\n result.push(ix);\n }\n\n return result.map((value) => value - 1);\n }\n\n /**\n *\n * @param input\n * @returns {*}\n */\n runInput(input) {\n this.outputs[0] = input; // set output state of input layer\n\n let output = null;\n for (let layer = 1; layer <= this.outputLayer; layer++) {\n for (let node = 0; node < this.sizes[layer]; node++) {\n let weights = this.weights[layer][node];\n\n let sum = this.biases[layer][node];\n for (let k = 0; k < weights.length; k++) {\n sum += weights[k] * input[k];\n }\n this.outputs[layer][node] = 1 / (1 + Math.exp(-sum));\n }\n output = input = this.outputs[layer];\n }\n return output;\n }\n\n /**\n *\n * @param data\n * @param options\n * @returns {{error: number, iterations: number}}\n */\n /*train(data, options) {\n throw new Error('not yet implemented');\n //data = this.formatData(data);\n\n options = options || {};\n let iterations = options.iterations || 20000;\n let errorThresh = options.errorThresh || 0.005;\n let log = options.log ? (typeof options.log === 'function' ? options.log : console.log) : false;\n let logPeriod = options.logPeriod || 10;\n let learningRate = options.learningRate || this.learningRate || 0.3;\n let callback = options.callback;\n let callbackPeriod = options.callbackPeriod || 10;\n let sizes = [];\n let inputSize = data[0].input.length;\n let outputSize = data[0].output.length;\n let hiddenSizes = this.hiddenSizes;\n if (!hiddenSizes) {\n sizes.push(Math.max(3, Math.floor(inputSize / 2)));\n } else {\n hiddenSizes.forEach(function(size) {\n sizes.push(size);\n });\n }\n\n sizes.unshift(inputSize);\n sizes.push(outputSize);\n\n //this.initialize(sizes, options.keepNetworkIntact);\n\n let error = 1;\n for (let i = 0; i < iterations && error > errorThresh; i++) {\n let sum = 0;\n for (let j = 0; j < data.length; j++) {\n let err = this.trainPattern(data[j].input, data[j].output, learningRate);\n sum += err;\n }\n error = sum / data.length;\n\n if (log && (i % logPeriod == 0)) {\n log('iterations:', i, 'training error:', error);\n }\n if (callback && (i % callbackPeriod == 0)) {\n callback({ error: error, iterations: i });\n }\n }\n\n return {\n error: error,\n iterations: i\n };\n }*/\n\n /**\n *\n * @param input\n * @param target\n * @param learningRate\n */\n trainPattern(input, target, learningRate) {\n throw new Error('not yet implemented');\n }\n\n /**\n *\n * @param target\n */\n calculateDeltas(target) {\n throw new Error('not yet implemented');\n }\n\n /**\n *\n * @param learningRate\n */\n adjustWeights(learningRate) {\n throw new Error('not yet implemented');\n }\n\n /**\n *\n * @param data\n * @returns {*}\n */\n formatData(data) {\n throw new Error('not yet implemented');\n }\n\n /**\n *\n * @param data\n * @returns {\n * {\n * error: number,\n * misclasses: Array\n * }\n * }\n */\n test(data) {\n throw new Error('not yet implemented');\n }\n\n toJSON() {\n let model = this.model;\n let options = {};\n for (let p in defaults) {\n options[p] = this[p];\n }\n\n return {\n type: this.constructor.name,\n options: options,\n input: model.input.toJSON(),\n hiddenLayers: model.hiddenLayers.map((hiddenLayer) => {\n let layers = {};\n for (let p in hiddenLayer) {\n layers[p] = hiddenLayer[p].toJSON();\n }\n return layers;\n }),\n outputConnector: this.model.outputConnector.toJSON(),\n output: this.model.output.toJSON()\n };\n }\n\n fromJSON(json) {\n this.json = json;\n let model = this.model;\n let options = json.options;\n let allMatrices = model.allMatrices;\n model.input = Matrix.fromJSON(json.input);\n allMatrices.push(model.input);\n model.hiddenLayers = json.hiddenLayers.map((hiddenLayer) => {\n let layers = {};\n for (let p in hiddenLayer) {\n layers[p] = Matrix.fromJSON(hiddenLayer[p]);\n allMatrices.push(layers[p]);\n }\n return layers;\n });\n model.outputConnector = Matrix.fromJSON(json.outputConnector);\n model.output = Matrix.fromJSON(json.output);\n allMatrices.push(model.outputConnector, model.output);\n\n for (let p in defaults) {\n if (defaults.hasOwnProperty(p) && p !== 'isBackPropagate') {\n this[p] = options.hasOwnProperty(p) ? options[p] : defaults[p];\n }\n }\n\n this.bindEquation();\n }\n\n /**\n *\n * @returns {Function}\n */\n toFunction() {\n let model = this.model;\n let equations = this.model.equations;\n let equation = equations[1];\n let states = equation.states;\n let modelAsString = JSON.stringify(this.toJSON());\n\n function matrixOrigin(m, stateIndex) {\n for (let i = 0, max = states.length; i < max; i++) {\n let state = states[i];\n\n if (i === stateIndex) {\n let j = previousConnectionIndex(m);\n switch (m) {\n case state.left:\n if (j > -1) {\n return `typeof prevStates[${ j }] === 'object' ? prevStates[${ j }].product : new Matrix(${ m.rows }, ${ m.columns })`;\n }\n case state.right:\n if (j > -1) {\n return `typeof prevStates[${ j }] === 'object' ? prevStates[${ j }].product : new Matrix(${ m.rows }, ${ m.columns })`;\n }\n case state.product:\n return `new Matrix(${ m.rows }, ${ m.columns })`;\n default:\n throw Error('unknown state');\n }\n }\n\n if (m === state.product) return `states[${ i }].product`;\n if (m === state.right) return `states[${ i }].right`;\n if (m === state.left) return `states[${ i }].left`;\n }\n }\n\n function previousConnectionIndex(m) {\n const connection = model.equationConnections[0];\n const states = equations[0].states;\n for (let i = 0, max = states.length; i < max; i++) {\n if (states[i].product === m) {\n return i;\n }\n }\n return connection.indexOf(m);\n }\n\n function matrixToString(m, stateIndex) {\n if (!m || !m.rows || !m.columns) return 'null';\n\n if (m === model.input) return `model.input`;\n if (m === model.outputConnector) return `model.outputConnector`;\n if (m === model.output) return `model.output`;\n\n for (let i = 0, max = model.hiddenLayers.length; i < max; i++) {\n let hiddenLayer = model.hiddenLayers[i];\n for (let p in hiddenLayer) {\n if (!hiddenLayer.hasOwnProperty(p)) continue;\n if (hiddenLayer[p] !== m) continue;\n return `model.hiddenLayers[${ i }].${ p }`;\n }\n }\n\n return matrixOrigin(m, stateIndex);\n }\n\n function toInner(fnString) {\n //crude, but should be sufficient for now\n // function() { body }\n fnString = fnString.toString().split('{');\n fnString.shift();\n // body }\n fnString = fnString.join('{');\n fnString = fnString.split('}');\n fnString.pop();\n // body\n return fnString.join('}').split('\\n').join('\\n ');\n }\n\n function fileName(fnName) {\n return `src/recurrent/matrix/${ fnName.replace(/[A-Z]/g, function(value) { return '-' + value.toLowerCase(); }) }.js`;\n }\n\n let statesRaw = [];\n let usedFunctionNames = {};\n let innerFunctionsSwitch = [];\n for (let i = 0, max = states.length; i < max; i++) {\n let state = states[i];\n statesRaw.push(`states[${ i }] = {\n name: '${ state.forwardFn.name }',\n left: ${ matrixToString(state.left, i) },\n right: ${ matrixToString(state.right, i) },\n product: ${ matrixToString(state.product, i) }\n }`);\n\n let fnName = state.forwardFn.name;\n if (!usedFunctionNames[fnName]) {\n usedFunctionNames[fnName] = true;\n innerFunctionsSwitch.push(\n ` case '${ fnName }': //compiled from ${ fileName(fnName) }\n ${ toInner(state.forwardFn.toString()) }\n break;`\n );\n }\n }\n\n return new Function('input', 'maxPredictionLength', '_sampleI', 'temperature', `\n if (typeof input === 'undefined') input = [];\n if (typeof maxPredictionLength === 'undefined') maxPredictionLength = 100;\n if (typeof _sampleI === 'undefined') _sampleI = false;\n if (typeof temperature === 'undefined') temperature = 1;\n \n var model = ${ modelAsString };\n var _i = 0;\n var result = input.slice(0);\n var states = [];\n var prevStates;\n while (true) {\n // sample predicted letter\n var ix = result.length === 0 ? 0 : result[result.length - 1]; // first step: start with START token\n var rowPluckIndex = ix; //connect up to rowPluck\n prevStates = states;\n states = [];\n ${ statesRaw.join(';\\n ') };\n for (var stateIndex = 0, stateMax = ${ statesRaw.length }; stateIndex < stateMax; stateIndex++) {\n var state = states[stateIndex];\n var product = state.product;\n var left = state.left;\n var right = state.right;\n \n switch (state.name) {\n${ innerFunctionsSwitch.join('\\n') }\n }\n }\n \n var logProbabilities = state.product;\n if (temperature !== 1 && _sampleI) {\n // scale log probabilities by temperature and renormalize\n // if temperature is high, logprobs will go towards zero\n // and the softmax outputs will be more diffuse. if temperature is\n // very low, the softmax outputs will be more peaky\n for (var q = 0, nq = logProbabilities.weights.length; q < nq; q++) {\n logProbabilities.weights[q] /= temperature;\n }\n }\n\n var probs = softmax(logProbabilities);\n\n if (_sampleI) {\n ix = sampleI(probs);\n } else {\n ix = maxI(probs);\n }\n \n _i++;\n if (ix === 0) {\n // END token predicted, break out\n break;\n }\n if (_i >= maxPredictionLength) {\n // something is wrong\n break;\n }\n\n result.push(ix);\n }\n\n return result.map(function(value) { return value - 1; });\n \n function Matrix(rows, columns) {\n this.rows = rows;\n this.columns = columns;\n this.weights = zeros(rows * columns);\n this.recurrence = zeros(rows * columns);\n }\n ${ zeros.toString() }\n ${ softmax.toString() }\n ${ randomF.toString() }\n ${ sampleI.toString() }\n ${ maxI.toString() }`);\n }\n}\n"]} \ No newline at end of file diff --git a/dist/train-stream.js b/dist/train-stream.js index d1d0f2a02..4505c5570 100644 --- a/dist/train-stream.js +++ b/dist/train-stream.js @@ -227,4 +227,4 @@ function uniques(arr) { } return a; } -//# sourceMappingURL=train-stream.js.map +//# sourceMappingURL=train-stream.js.map \ No newline at end of file diff --git a/dist/utilities/max.js b/dist/utilities/max.js index 5eab84990..468374e26 100644 --- a/dist/utilities/max.js +++ b/dist/utilities/max.js @@ -19,4 +19,4 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de function max(values) { return Math.max.apply(Math, (0, _toArray2.default)(values)); } -//# sourceMappingURL=max.js.map +//# sourceMappingURL=max.js.map \ No newline at end of file diff --git a/dist/utilities/mse.js b/dist/utilities/mse.js index d8f70231b..3a2ec801b 100644 --- a/dist/utilities/mse.js +++ b/dist/utilities/mse.js @@ -12,4 +12,4 @@ function mse(errors) { } return sum / errors.length; } -//# sourceMappingURL=mse.js.map +//# sourceMappingURL=mse.js.map \ No newline at end of file diff --git a/dist/utilities/ones.js b/dist/utilities/ones.js new file mode 100644 index 000000000..4fea83a9a --- /dev/null +++ b/dist/utilities/ones.js @@ -0,0 +1,15 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = ones; +function ones(size) { + if (typeof Float64Array !== 'undefined') return new Float64Array(size).fill(1); + var array = new Array(size); + for (var i = 0; i < size; i++) { + array[i] = i; + } + return array; +} +//# sourceMappingURL=ones.js.map \ No newline at end of file diff --git a/dist/utilities/ones.js.map b/dist/utilities/ones.js.map new file mode 100644 index 000000000..43a04616e --- /dev/null +++ b/dist/utilities/ones.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../src/utilities/ones.js"],"names":["ones","size","Float64Array","fill","array","Array","i"],"mappings":";;;;;kBAAwBA,I;AAAT,SAASA,IAAT,CAAcC,IAAd,EAAoB;AACjC,MAAI,OAAOC,YAAP,KAAwB,WAA5B,EAAyC,OAAO,IAAIA,YAAJ,CAAiBD,IAAjB,EAAuBE,IAAvB,CAA4B,CAA5B,CAAP;AACzC,MAAIC,QAAQ,IAAIC,KAAJ,CAAUJ,IAAV,CAAZ;AACA,OAAK,IAAIK,IAAI,CAAb,EAAgBA,IAAIL,IAApB,EAA0BK,GAA1B,EAA+B;AAC7BF,UAAME,CAAN,IAAWA,CAAX;AACD;AACD,SAAOF,KAAP;AACD","file":"ones.js","sourcesContent":["export default function ones(size) {\n if (typeof Float64Array !== 'undefined') return new Float64Array(size).fill(1);\n let array = new Array(size);\n for (let i = 0; i < size; i++) {\n array[i] = i;\n }\n return array;\n}\n"]} \ No newline at end of file diff --git a/dist/utilities/random-weight.js b/dist/utilities/random-weight.js index 04d8bff4e..fb637ba80 100644 --- a/dist/utilities/random-weight.js +++ b/dist/utilities/random-weight.js @@ -7,4 +7,4 @@ exports.default = randomWeight; function randomWeight() { return Math.random() * 0.4 - 0.2; } -//# sourceMappingURL=random-weight.js.map +//# sourceMappingURL=random-weight.js.map \ No newline at end of file diff --git a/dist/utilities/random.js b/dist/utilities/random.js new file mode 100644 index 000000000..d3a6439be --- /dev/null +++ b/dist/utilities/random.js @@ -0,0 +1,40 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.randomF = randomF; +exports.randomI = randomI; +exports.randomN = randomN; +function randomF(a, b) { + return Math.random() * (b - a) + a; +} + +function randomI(a, b) { + return Math.floor(Math.random() * (b - a) + a); +} + +function randomN(mu, std) { + return mu + gaussRandom() * std; +} + +// Random numbers utils +function gaussRandom() { + if (gaussRandom.returnV) { + gaussRandom.returnV = false; + return gaussRandom.vVal; + } + var u = 2 * Math.random() - 1; + var v = 2 * Math.random() - 1; + var r = u * u + v * v; + if (r == 0 || r > 1) { + return gaussRandom(); + } + var c = Math.sqrt(-2 * Math.log(r) / r); + gaussRandom.vVal = v * c; // cache this + gaussRandom.returnV = true; + return u * c; +} +gaussRandom.returnV = false; +gaussRandom.vVal = 0; +//# sourceMappingURL=random.js.map \ No newline at end of file diff --git a/dist/utilities/random.js.map b/dist/utilities/random.js.map new file mode 100644 index 000000000..71a620f08 --- /dev/null +++ b/dist/utilities/random.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../src/utilities/random.js"],"names":["randomF","randomI","randomN","a","b","Math","random","floor","mu","std","gaussRandom","returnV","vVal","u","v","r","c","sqrt","log"],"mappings":";;;;;QAAgBA,O,GAAAA,O;QAIAC,O,GAAAA,O;QAIAC,O,GAAAA,O;AART,SAASF,OAAT,CAAiBG,CAAjB,EAAoBC,CAApB,EAAuB;AAC5B,SAAOC,KAAKC,MAAL,MAAiBF,IAAID,CAArB,IAA0BA,CAAjC;AACD;;AAEM,SAASF,OAAT,CAAiBE,CAAjB,EAAoBC,CAApB,EAAuB;AAC5B,SAAOC,KAAKE,KAAL,CAAWF,KAAKC,MAAL,MAAiBF,IAAID,CAArB,IAA0BA,CAArC,CAAP;AACD;;AAEM,SAASD,OAAT,CAAiBM,EAAjB,EAAqBC,GAArB,EAA0B;AAC/B,SAAOD,KAAKE,gBAAgBD,GAA5B;AACD;;AAED;AACA,SAASC,WAAT,GAAuB;AACrB,MAAIA,YAAYC,OAAhB,EAAyB;AACvBD,gBAAYC,OAAZ,GAAsB,KAAtB;AACA,WAAOD,YAAYE,IAAnB;AACD;AACD,MAAIC,IAAI,IAAIR,KAAKC,MAAL,EAAJ,GAAoB,CAA5B;AACA,MAAIQ,IAAI,IAAIT,KAAKC,MAAL,EAAJ,GAAoB,CAA5B;AACA,MAAIS,IAAIF,IAAIA,CAAJ,GAAQC,IAAIA,CAApB;AACA,MAAIC,KAAK,CAAL,IAAUA,IAAI,CAAlB,EAAqB;AACnB,WAAOL,aAAP;AACD;AACD,MAAIM,IAAIX,KAAKY,IAAL,CAAU,CAAC,CAAD,GAAKZ,KAAKa,GAAL,CAASH,CAAT,CAAL,GAAmBA,CAA7B,CAAR;AACAL,cAAYE,IAAZ,GAAmBE,IAAIE,CAAvB,CAZqB,CAYK;AAC1BN,cAAYC,OAAZ,GAAsB,IAAtB;AACA,SAAOE,IAAIG,CAAX;AACD;AACDN,YAAYC,OAAZ,GAAsB,KAAtB;AACAD,YAAYE,IAAZ,GAAmB,CAAnB","file":"random.js","sourcesContent":["export function randomF(a, b) {\n return Math.random() * (b - a) + a;\n}\n\nexport function randomI(a, b) {\n return Math.floor(Math.random() * (b - a) + a);\n}\n\nexport function randomN(mu, std) {\n return mu + gaussRandom() * std;\n}\n\n// Random numbers utils\nfunction gaussRandom() {\n if (gaussRandom.returnV) {\n gaussRandom.returnV = false;\n return gaussRandom.vVal;\n }\n let u = 2 * Math.random() - 1;\n let v = 2 * Math.random() - 1;\n let r = u * u + v * v;\n if (r == 0 || r > 1) {\n return gaussRandom();\n }\n let c = Math.sqrt(-2 * Math.log(r) / r);\n gaussRandom.vVal = v * c; // cache this\n gaussRandom.returnV = true;\n return u * c;\n}\ngaussRandom.returnV = false;\ngaussRandom.vVal = 0;\n"]} \ No newline at end of file diff --git a/dist/utilities/randos.js b/dist/utilities/randos.js index b01cba9f4..3b0ad0f24 100644 --- a/dist/utilities/randos.js +++ b/dist/utilities/randos.js @@ -18,4 +18,4 @@ function randos(size) { } return array; } -//# sourceMappingURL=randos.js.map +//# sourceMappingURL=randos.js.map \ No newline at end of file diff --git a/dist/utilities/range.js b/dist/utilities/range.js index b8210fb76..d2c42af65 100644 --- a/dist/utilities/range.js +++ b/dist/utilities/range.js @@ -17,4 +17,4 @@ function range(start, end) { } return result; } -//# sourceMappingURL=range.js.map +//# sourceMappingURL=range.js.map \ No newline at end of file diff --git a/dist/utilities/to-array.js b/dist/utilities/to-array.js index 70ed8322f..51992af0d 100644 --- a/dist/utilities/to-array.js +++ b/dist/utilities/to-array.js @@ -19,4 +19,4 @@ function toArray(values) { }); } } -//# sourceMappingURL=to-array.js.map +//# sourceMappingURL=to-array.js.map \ No newline at end of file diff --git a/dist/utilities/vocab.js b/dist/utilities/vocab.js new file mode 100644 index 000000000..16b1017d9 --- /dev/null +++ b/dist/utilities/vocab.js @@ -0,0 +1,120 @@ +'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 _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +/** + * + * @param {String[]|Number[]} values + * @param maxThreshold + * @constructor + */ +var Vocab = function () { + function Vocab(values) { + var maxThreshold = arguments.length <= 1 || arguments[1] === undefined ? 0 : arguments[1]; + + _classCallCheck(this, Vocab); + + this.values = values; + // go over all characters and keep track of all unique ones seen + // count up all characters + this.indexTable = {}; + this.characterTable = {}; + this.characters = []; + var tempCharactersTable = {}; + for (var vocabIndex = 0, vocabLength = values.length; vocabIndex < vocabLength; vocabIndex++) { + var characters = values[vocabIndex].toString(); + for (var characterIndex = 0, _charactersLength = characters.length; characterIndex < _charactersLength; characterIndex++) { + var character = characters[characterIndex]; + if (character in tempCharactersTable) continue; + tempCharactersTable[character] = true; + this.characters.push(character); + } + } + + // filter by count threshold and create pointers + + // NOTE: start at one because we will have START and END tokens! + // that is, START token will be index 0 in model letter vectors + // and END token will be index 0 in the next character softmax + var charactersLength = this.characters.length; + for (var _characterIndex = 0; _characterIndex < charactersLength; _characterIndex++) { + var _character = this.characters[_characterIndex]; + if (_characterIndex >= maxThreshold) { + // add character to vocab + this.indexTable[_character] = _characterIndex; + this.characterTable[_characterIndex] = _character; + } + } + } + + _createClass(Vocab, [{ + key: 'toIndexes', + value: function toIndexes(phrase) { + var maxThreshold = arguments.length <= 1 || arguments[1] === undefined ? 0 : arguments[1]; + + var result = []; + var indexTable = this.indexTable; + + for (var i = 0, max = phrase.length; i < max; i++) { + var character = phrase[i]; + var index = indexTable[character]; + if (index < maxThreshold) continue; + result.push(index); + } + + return result; + } + }, { + key: 'toCharacters', + value: function toCharacters(indexes) { + var maxThreshold = arguments.length <= 1 || arguments[1] === undefined ? 0 : arguments[1]; + + var result = []; + var characterTable = this.characterTable; + + for (var i = 0, max = indexes.length; i < max; i++) { + var index = indexes[i]; + if (index < maxThreshold) continue; + var character = characterTable[index]; + result.push(character); + } + + return result; + } + }, { + key: 'toString', + value: function toString(indexes, maxThreshold) { + return this.toCharacters(indexes, maxThreshold).join(''); + } + }], [{ + key: 'allPrintable', + value: function allPrintable(maxThreshold) { + var values = []; + for (var i = 32; i <= 126; i++) { + values.push(String.fromCharCode(i)); + } + return new Vocab(values, maxThreshold); + } + }, { + key: 'fromString', + value: function fromString(string, maxThreshold) { + var _String$prototype; + + var values = (_String$prototype = String.prototype).concat.apply(_String$prototype, _toConsumableArray(new Set(string))); + return new Vocab(values, maxThreshold); + } + }]); + + return Vocab; +}(); + +exports.default = Vocab; +//# sourceMappingURL=vocab.js.map \ No newline at end of file diff --git a/dist/utilities/vocab.js.map b/dist/utilities/vocab.js.map new file mode 100644 index 000000000..1ffeae7f7 --- /dev/null +++ b/dist/utilities/vocab.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../src/utilities/vocab.js"],"names":["Vocab","values","maxThreshold","indexTable","characterTable","characters","tempCharactersTable","vocabIndex","vocabLength","length","toString","characterIndex","charactersLength","character","push","phrase","result","i","max","index","indexes","toCharacters","join","String","fromCharCode","string","prototype","concat","Set"],"mappings":"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","file":"vocab.js","sourcesContent":["/**\n *\n * @param {String[]|Number[]} values\n * @param maxThreshold\n * @constructor\n */\nexport default class Vocab {\n constructor(values, maxThreshold = 0) {\n this.values = values;\n // go over all characters and keep track of all unique ones seen\n // count up all characters\n this.indexTable = {};\n this.characterTable = {};\n this.characters = [];\n let tempCharactersTable = {};\n for (let vocabIndex = 0, vocabLength = values.length; vocabIndex < vocabLength; vocabIndex++) {\n var characters = values[vocabIndex].toString();\n for (let characterIndex = 0, charactersLength = characters.length; characterIndex < charactersLength; characterIndex++) {\n let character = characters[characterIndex];\n if (character in tempCharactersTable) continue;\n tempCharactersTable[character] = true;\n this.characters.push(character);\n }\n }\n\n // filter by count threshold and create pointers\n\n // NOTE: start at one because we will have START and END tokens!\n // that is, START token will be index 0 in model letter vectors\n // and END token will be index 0 in the next character softmax\n let charactersLength = this.characters.length;\n for(let characterIndex = 0; characterIndex < charactersLength; characterIndex++) {\n let character = this.characters[characterIndex];\n if(characterIndex >= maxThreshold) {\n // add character to vocab\n this.indexTable[character] = characterIndex;\n this.characterTable[characterIndex] = character;\n }\n }\n }\n\n toIndexes(phrase, maxThreshold = 0) {\n let result = [];\n let indexTable = this.indexTable;\n\n for (let i = 0, max = phrase.length; i < max; i++) {\n let character = phrase[i];\n let index = indexTable[character];\n if (index < maxThreshold) continue;\n result.push(index);\n }\n\n return result;\n }\n\n toCharacters(indexes, maxThreshold = 0) {\n let result = [];\n let characterTable = this.characterTable;\n\n for (let i = 0, max = indexes.length; i < max; i++) {\n let index = indexes[i];\n if (index < maxThreshold) continue;\n let character = characterTable[index];\n result.push(character);\n }\n\n return result;\n }\n\n toString(indexes, maxThreshold) {\n return this.toCharacters(indexes, maxThreshold).join('');\n }\n\n static allPrintable(maxThreshold) {\n const values = [];\n for(let i = 32; i <= 126; i++) {\n values.push(String.fromCharCode(i));\n }\n return new Vocab(values, maxThreshold);\n }\n\n static fromString(string, maxThreshold) {\n const values = String.prototype.concat(...new Set(string));\n return new Vocab(values, maxThreshold);\n }\n}\n"]} \ No newline at end of file diff --git a/dist/utilities/zeros.js b/dist/utilities/zeros.js index 8b0538cd8..fe45a70eb 100644 --- a/dist/utilities/zeros.js +++ b/dist/utilities/zeros.js @@ -1,14 +1,15 @@ -"use strict"; +'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = zeros; function zeros(size) { + if (typeof Float64Array !== 'undefined') return new Float64Array(size); var array = new Array(size); for (var i = 0; i < size; i++) { array[i] = 0; } return array; } -//# sourceMappingURL=zeros.js.map +//# sourceMappingURL=zeros.js.map \ No newline at end of file diff --git a/dist/utilities/zeros.js.map b/dist/utilities/zeros.js.map index 923ff388e..c55e5f490 100644 --- a/dist/utilities/zeros.js.map +++ b/dist/utilities/zeros.js.map @@ -1 +1 @@ -{"version":3,"sources":["../../src/utilities/zeros.js"],"names":["zeros","size","array","Array","i"],"mappings":";;;;;kBAAwBA,K;AAAT,SAASA,KAAT,CAAeC,IAAf,EAAqB;AAClC,MAAIC,QAAQ,IAAIC,KAAJ,CAAUF,IAAV,CAAZ;AACA,OAAK,IAAIG,IAAI,CAAb,EAAgBA,IAAIH,IAApB,EAA0BG,GAA1B,EAA+B;AAC7BF,UAAME,CAAN,IAAW,CAAX;AACD;AACD,SAAOF,KAAP;AACD","file":"zeros.js","sourcesContent":["export default function zeros(size) {\n let array = new Array(size);\n for (let i = 0; i < size; i++) {\n array[i] = 0;\n }\n return array;\n}\n"]} \ No newline at end of file +{"version":3,"sources":["../../src/utilities/zeros.js"],"names":["zeros","size","Float64Array","array","Array","i"],"mappings":";;;;;kBAAwBA,K;AAAT,SAASA,KAAT,CAAeC,IAAf,EAAqB;AAClC,MAAI,OAAOC,YAAP,KAAwB,WAA5B,EAAyC,OAAO,IAAIA,YAAJ,CAAiBD,IAAjB,CAAP;AACzC,MAAIE,QAAQ,IAAIC,KAAJ,CAAUH,IAAV,CAAZ;AACA,OAAK,IAAII,IAAI,CAAb,EAAgBA,IAAIJ,IAApB,EAA0BI,GAA1B,EAA+B;AAC7BF,UAAME,CAAN,IAAW,CAAX;AACD;AACD,SAAOF,KAAP;AACD","file":"zeros.js","sourcesContent":["export default function zeros(size) {\n if (typeof Float64Array !== 'undefined') return new Float64Array(size);\n let array = new Array(size);\n for (let i = 0; i < size; i++) {\n array[i] = 0;\n }\n return array;\n}\n"]} \ No newline at end of file diff --git a/examples/stream-example.js b/examples/cli/stream-example.js similarity index 100% rename from examples/stream-example.js rename to examples/cli/stream-example.js diff --git a/examples/html/lstm-math/index.html b/examples/html/lstm-math/index.html new file mode 100644 index 000000000..5264601b1 --- /dev/null +++ b/examples/html/lstm-math/index.html @@ -0,0 +1,331 @@ + + + + + + +
+ + + + + + + + + + + + +
CorrectCorrect Pattern, Wrong MathWrong Pattern
+

+    
+

+    
+

+    
+

+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/index.js b/index.js
index 553771db2..a2da5bafc 100644
--- a/index.js
+++ b/index.js
@@ -1,13 +1,49 @@
-import crossValidate from './dist/cross-validate';
-import likely from './dist/likely';
-import lookup from './dist/lookup';
-import NeuralNetwork from './dist/neural-network';
-import TrainStream from './dist/train-stream';
+var crossValidate = require('./dist/cross-validate');
+var likely = require('./dist/likely');
+var lookup = require('./dist/lookup');
+var NeuralNetwork = require('./dist/neural-network');
+var TrainStream = require('./dist/train-stream');
+var RNN = require('./dist/recurrent/rnn');
+var LSTM = require('./dist/recurrent/lstm');
+var GRU = require('./dist/recurrent/gru');
+var utilities = {
+  max: require('./dist/utilities/max'),
+  mse: require('./dist/utilities/mse'),
+  ones: require('./dist/utilities/ones'),
+  random: require('./dist/utilities/random'),
+  randomWeight: require('./dist/utilities/random-weight'),
+  randos: require('./dist/utilities/randos'),
+  range: require('./dist/utilities/range'),
+  toArray: require('./dist/utilities/to-array'),
+  Vocab: require('./dist/utilities/vocab'),
+  zeros: require('./dist/utilities/zeros')
+};
 
-export default {
-  crossValidate,
-  likely,
-  lookup,
-  NeuralNetwork,
-  TrainStream
+module.exports = {
+  crossValidate: crossValidate,
+  likely: likely,
+  lookup: lookup,
+  NeuralNetwork: NeuralNetwork,
+  TrainStream: TrainStream,
+  recurrent: {
+    RNN: RNN,
+    LSTM: LSTM,
+    GRU: GRU,
+  },
+  utilities: utilities
 };
+
+if (typeof window !== 'undefined') {
+  var brain = window.brain = {};
+  var i;
+
+  for (i in module.exports) {
+    brain[i] = module.exports[i].default || module.exports[i];
+  }
+  for (i in module.exports.utilities) {
+    brain.utilities[i] = module.exports.utilities[i].default || module.exports.utilities[i];
+  }
+  for (i in module.exports.recurrent) {
+    brain.recurrent[i] = module.exports.recurrent[i].default || module.exports.recurrent[i];
+  }
+}
diff --git a/manual-lstm.js b/manual-lstm.js
new file mode 100644
index 000000000..b64356405
--- /dev/null
+++ b/manual-lstm.js
@@ -0,0 +1,37 @@
+import assert from 'assert';
+import LSTM from './src/recurrent/lstm';
+import { vocab, build, trainUntil } from './test/utilities/math-addition-vocab';
+
+let mathProblems = build();
+function runAgainstMath(rnn) {
+  var thresh = 0;
+  trainUntil(rnn, (out) => {
+    var parts = out.split(/[+=]/g);
+    if (parts.length !== 3) return;
+
+    if (parseInt(parts[0]) + parseInt(parts[1]) === parseInt(parts[2])) {
+      thresh++;
+    }
+
+    if (thresh > 100) {
+      return true;
+    }
+  });
+  var prediction = vocab.toCharacters(rnn.predict()).join('');
+  console.log(prediction);
+  assert(/[+]/.test(prediction));
+  assert(/[=]/.test(prediction));
+}
+
+
+console.time('math lstm');
+var lstm = new LSTM({
+  inputSize: 6,
+  inputRange: vocab.characters.length,
+  outputSize: vocab.characters.length
+});
+
+runAgainstMath(lstm);
+
+console.timeEnd('math lstm');
+console.log('');
diff --git a/package.json b/package.json
index 14eb9ebdf..ce7a7027c 100644
--- a/package.json
+++ b/package.json
@@ -1,33 +1,40 @@
 {
   "name": "brain.js",
   "description": "Neural network library",
-  "version": "1.0.0-alpha",
+  "version": "1.0.0",
   "author": "Heather Arthur ",
   "repository": {
     "type": "git",
     "url": "git+ssh://git@github.com/harthur-org/brain.js.git"
   },
+  "scripts": {
+    "test-base": "find ./test/base/ -name '*.js' | xargs mocha --compilers js:babel-core/register",
+    "test-base-cv": "find ./test/base/cross-validation/ -name '*.js' | xargs mocha --compilers js:babel-core/register",
+    "test-recurrent": "find ./test/recurrent/ -name '*.js' | xargs mocha --compilers js:babel-core/register",
+    "test-utilities": "find ./test/utilities/ -name '*.js' | xargs mocha --compilers js:babel-core/register",
+    "test": "npm run test-base && npm run test-base-cv && npm run test-recurrent && npm run test-utilities",
+    "dist": "babel src --out-dir dist --source-maps",
+    "browser": "browserify ./index.js -p licensify -o browser.js",
+    "browser-min": "browserify ./index.js -p licensify -g uglifyify -o browser.min.js",
+    "make": "rm -fr ./dist && npm run dist && git add ./dist && npm run browser && npm run browser-min"
+  },
   "main": "./index.js",
   "devDependencies": {
+    "babel-core": "^6.14.0",
     "babel-preset-es2015": "^6.14.0",
-    "babel-register": "^6.14.0",
-    "browserify": "~3.46.1",
-    "canvas": "~1.3.12",
-    "grunt": "^0.4.5",
-    "grunt-babel": "^6.0.0",
-    "grunt-browserify": "^5.0.0",
-    "grunt-cli": "^1.2.0",
-    "grunt-contrib-uglify": "~0.2.7",
-    "grunt-mocha-test": "~0.11.0",
-    "load-grunt-tasks": "^3.5.2",
-    "mocha": "^3.0.2"
+    "browserify": "^13.1.0",
+    "canvas": "^1.4.0",
+    "licensify": "^3.1.2",
+    "mocha": "^3.0.2",
+    "sinon": "^1.17.6",
+    "uglifyify": "^3.0.2"
   },
   "keywords": [
     "ai",
     "artificial-intelligence",
     "brain",
     "brainjs",
-    "brain,js",
+    "brain.js",
     "feed forward",
     "neural network",
     "classifier",
@@ -35,17 +42,18 @@
     "network",
     "neural-networks",
     "machine-learning",
-    "synpapse"
+    "synapse"
   ],
   "bugs": {
     "url": "https://github.com/harthur-org/brain.js/issues"
   },
-  "homepage": "https://github.com/harthur-org/brain.jsreadme",
+  "homepage": "https://github.com/harthur-org/brain.js#readme",
   "directories": {
     "test": "test"
   },
   "license": "MIT",
   "dependencies": {
-    "babel-plugin-transform-object-rest-spread": "^6.8.0"
+    "babel-plugin-transform-object-rest-spread": "^6.8.0",
+    "babel-polyfill": "^6.13.0"
   }
 }
diff --git a/src/index.js b/src/index.js
index 99a330e24..e7e2824e0 100644
--- a/src/index.js
+++ b/src/index.js
@@ -3,11 +3,19 @@ import likely from './likely';
 import lookup from './lookup';
 import NeuralNetwork from './neural-network';
 import TrainStream from './train-stream';
+import RNN from './recurrent/rnn';
+import LSTM from './recurrent/lstm';
+import GRU from './recurrent/gru';
 
 export default {
   crossValidate,
   likely,
   lookup,
   NeuralNetwork,
-  TrainStream
+  TrainStream,
+  recurrent: {
+    RNN,
+    LSTM,
+    GRU
+  }
 };
diff --git a/src/neural-network.js b/src/neural-network.js
index d5f23081b..cc421dc7a 100644
--- a/src/neural-network.js
+++ b/src/neural-network.js
@@ -127,10 +127,9 @@ export default class NeuralNetwork {
    * @param options
    * @returns {{error: number, iterations: number}}
    */
-  train(data, options) {
+  train(data, options = {}) {
     data = this.formatData(data);
 
-    options = options || {};
     let iterations = options.iterations || 20000;
     let errorThresh = options.errorThresh || 0.005;
     let log = options.log ? (typeof options.log === 'function' ? options.log : console.log) : false;
diff --git a/src/recurrent/gru.js b/src/recurrent/gru.js
new file mode 100644
index 000000000..fdfab7135
--- /dev/null
+++ b/src/recurrent/gru.js
@@ -0,0 +1,121 @@
+import Matrix from './matrix';
+import RandomMatrix from './matrix/random-matrix';
+import RNN from './rnn';
+
+export default class GRU extends RNN {
+  getModel(hiddenSize, prevSize) {
+    return {
+      // update Gate
+      //wzxh
+      updateGateInputMatrix: new RandomMatrix(hiddenSize, prevSize, 0.08),
+      //wzhh
+      updateGateHiddenMatrix: new RandomMatrix(hiddenSize, hiddenSize, 0.08),
+      //bz
+      updateGateBias: new Matrix(hiddenSize, 1),
+
+      // reset Gate
+      //wrxh
+      resetGateInputMatrix: new RandomMatrix(hiddenSize, prevSize, 0.08),
+      //wrhh
+      resetGateHiddenMatrix: new RandomMatrix(hiddenSize, hiddenSize, 0.08),
+      //br
+      resetGateBias: new Matrix(hiddenSize, 1),
+
+      // cell write parameters
+      //wcxh
+      cellWriteInputMatrix: new RandomMatrix(hiddenSize, prevSize, 0.08),
+      //wchh
+      cellWriteHiddenMatrix: new RandomMatrix(hiddenSize, hiddenSize, 0.08),
+      //bc
+      cellWriteBias: new Matrix(hiddenSize, 1)
+    };
+  }
+
+  /**
+   *
+   * @param {Equation} equation
+   * @param {Matrix} inputMatrix
+   * @param {Matrix} previousResult
+   * @param {Object} hiddenLayer
+   * @returns {Matrix}
+   */
+  getEquation(equation, inputMatrix, previousResult, hiddenLayer) {
+    let sigmoid = equation.sigmoid.bind(equation);
+    let add = equation.add.bind(equation);
+    let multiply = equation.multiply.bind(equation);
+    let multiplyElement = equation.multiplyElement.bind(equation);
+    let tanh = equation.tanh.bind(equation);
+    let allOnes = equation.allOnes.bind(equation);
+    let cloneNegative = equation.cloneNegative.bind(equation);
+
+    // update gate
+    let updateGate = sigmoid(
+      add(
+        add(
+          multiply(
+            hiddenLayer.updateGateInputMatrix,
+            inputMatrix
+          ),
+          multiply(
+            hiddenLayer.updateGateHiddenMatrix,
+            previousResult
+          )
+        ),
+        hiddenLayer.updateGateBias
+      )
+    );
+
+    // reset gate
+    let resetGate = sigmoid(
+        add(
+          add(
+            multiply(
+              hiddenLayer.resetGateInputMatrix,
+              inputMatrix
+            ),
+            multiply(
+              hiddenLayer.resetGateHiddenMatrix,
+              previousResult
+            )
+          ),
+          hiddenLayer.resetGateBias
+        )
+    );
+
+    // cell
+    let cell = tanh(
+      add(
+        add(
+          multiply(
+            hiddenLayer.cellWriteInputMatrix,
+            inputMatrix
+          ),
+          multiply(
+            hiddenLayer.cellWriteHiddenMatrix,
+            multiplyElement(
+              resetGate,
+              previousResult
+            )
+          )
+        ),
+        hiddenLayer.cellWriteBias
+      )
+    );
+
+    // compute hidden state as gated, saturated cell activations
+    // negate updateGate
+    return add(
+      multiplyElement(
+        add(
+          allOnes(updateGate.rows, updateGate.columns),
+          cloneNegative(updateGate)
+        ),
+        cell
+      ),
+      multiplyElement(
+        previousResult,
+        updateGate
+      )
+    );
+  }
+}
diff --git a/src/recurrent/lstm.js b/src/recurrent/lstm.js
new file mode 100644
index 000000000..406436de3
--- /dev/null
+++ b/src/recurrent/lstm.js
@@ -0,0 +1,132 @@
+import Matrix from './matrix';
+import RandomMatrix from './matrix/random-matrix';
+import RNN from './rnn';
+
+export default class LSTM extends RNN {
+  getModel(hiddenSize, prevSize) {
+    return {
+      // gates parameters
+      //wix
+      inputMatrix: new RandomMatrix(hiddenSize, prevSize, 0.08),
+      //wih
+      inputHidden: new RandomMatrix(hiddenSize, hiddenSize, 0.08),
+      //bi
+      inputBias: new Matrix(hiddenSize, 1),
+
+      //wfx
+      forgetMatrix: new RandomMatrix(hiddenSize, prevSize, 0.08),
+      //wfh
+      forgetHidden: new RandomMatrix(hiddenSize, hiddenSize, 0.08),
+      //bf
+      forgetBias: new Matrix(hiddenSize, 1),
+
+      //wox
+      outputMatrix: new RandomMatrix(hiddenSize, prevSize, 0.08),
+      //woh
+      outputHidden: new RandomMatrix(hiddenSize, hiddenSize, 0.08),
+      //bo
+      outputBias: new Matrix(hiddenSize, 1),
+
+      // cell write params
+      //wcx
+      cellActivationMatrix: new RandomMatrix(hiddenSize, prevSize, 0.08),
+      //wch
+      cellActivationHidden: new RandomMatrix(hiddenSize, hiddenSize, 0.08),
+      //bc
+      cellActivationBias: new Matrix(hiddenSize, 1)
+    };
+  }
+
+  /**
+   *
+   * @param {Equation} equation
+   * @param {Matrix} inputMatrix
+   * @param {Matrix} previousResult
+   * @param {Object} hiddenLayer
+   * @returns {Matrix}
+   */
+  getEquation(equation, inputMatrix, previousResult, hiddenLayer) {
+    let sigmoid = equation.sigmoid.bind(equation);
+    let add = equation.add.bind(equation);
+    let multiply = equation.multiply.bind(equation);
+    let multiplyElement = equation.multiplyElement.bind(equation);
+    let tanh = equation.tanh.bind(equation);
+
+    let inputGate = sigmoid(
+      add(
+        add(
+          multiply(
+            hiddenLayer.inputMatrix,
+            inputMatrix
+          ),
+          multiply(
+            hiddenLayer.inputHidden,
+            previousResult
+          )
+        ),
+        hiddenLayer.inputBias
+      )
+    );
+
+    let forgetGate = sigmoid(
+      add(
+        add(
+          multiply(
+            hiddenLayer.forgetMatrix,
+            inputMatrix
+          ),
+          multiply(
+            hiddenLayer.forgetHidden,
+            previousResult
+          )
+        ),
+        hiddenLayer.forgetBias
+      )
+    );
+
+    // output gate
+    let outputGate = sigmoid(
+      add(
+        add(
+          multiply(
+            hiddenLayer.outputMatrix,
+            inputMatrix
+          ),
+          multiply(
+            hiddenLayer.outputHidden,
+            previousResult
+          )
+        ),
+        hiddenLayer.outputBias
+      )
+    );
+
+    // write operation on cells
+    let cellWrite = tanh(
+      add(
+        add(
+          multiply(
+            hiddenLayer.cellActivationMatrix,
+            inputMatrix
+          ),
+          multiply(
+            hiddenLayer.cellActivationHidden,
+            previousResult
+          )
+        ),
+        hiddenLayer.cellActivationBias
+      )
+    );
+
+    // compute new cell activation
+    let retainCell = multiplyElement(forgetGate, previousResult); // what do we keep from cell
+    let writeCell = multiplyElement(inputGate, cellWrite); // what do we write to cell
+    let cell = add(retainCell, writeCell); // new cell contents
+
+    // compute hidden state as gated, saturated cell activations
+    return multiplyElement(
+      outputGate,
+      tanh(cell)
+    );
+  }
+}
diff --git a/src/recurrent/matrix/add-b.js b/src/recurrent/matrix/add-b.js
new file mode 100644
index 000000000..dd94628b4
--- /dev/null
+++ b/src/recurrent/matrix/add-b.js
@@ -0,0 +1,12 @@
+/**
+ * adds {from} recurrence to {left} and {right} recurrence
+ * @param {Matrix} product
+ * @param {Matrix} left
+ * @param {Matrix} right
+ */
+export default function addB(product, left, right) {
+  for(let i = 0, max = product.recurrence.length; i < max; i++) {
+    left.recurrence[i] += product.recurrence[i];
+    right.recurrence[i] += product.recurrence[i];
+  }
+}
diff --git a/src/recurrent/matrix/add.js b/src/recurrent/matrix/add.js
new file mode 100644
index 000000000..930c9b958
--- /dev/null
+++ b/src/recurrent/matrix/add.js
@@ -0,0 +1,11 @@
+/**
+ * add {left} and {right} matrix weights into {into}
+ * @param {Matrix} product
+ * @param {Matrix} left
+ * @param {Matrix} right
+ */
+export default function add(product, left, right) {
+  for(let i = 0, max = left.weights.length; i < max; i++) {
+    product.weights[i] = left.weights[i] + right.weights[i];
+  }
+}
diff --git a/src/recurrent/matrix/all-ones.js b/src/recurrent/matrix/all-ones.js
new file mode 100644
index 000000000..b02a12e48
--- /dev/null
+++ b/src/recurrent/matrix/all-ones.js
@@ -0,0 +1,10 @@
+/**
+ * makes matrix weights and recurrence all ones
+ * @param {Matrix} product
+ */
+export default function allOnes(product) {
+  for(let i = 0, max = product.weights.length; i < max; i++) {
+    product.weights[i] = 1;
+    product.recurrence[i] = 0;
+  }
+}
diff --git a/src/recurrent/matrix/clone-negative.js b/src/recurrent/matrix/clone-negative.js
new file mode 100644
index 000000000..2fa0eafb2
--- /dev/null
+++ b/src/recurrent/matrix/clone-negative.js
@@ -0,0 +1,15 @@
+/**
+ *
+ * @param {Matrix} product
+ * @param {Matrix} left
+ */
+export default function cloneNegative(product, left) {
+  product.rows = parseInt(left.rows);
+  product.columns = parseInt(left.columns);
+  product.weights = left.weights.slice(0);
+  product.recurrence = left.recurrence.slice(0);
+  for (let i = 0, max = left.weights.length; i < max; i++) {
+    product.weights[i] = -left.weights[i];
+    product.recurrence[i] = 0;
+  }
+}
diff --git a/src/recurrent/matrix/clone.js b/src/recurrent/matrix/clone.js
new file mode 100644
index 000000000..8338d09d1
--- /dev/null
+++ b/src/recurrent/matrix/clone.js
@@ -0,0 +1,14 @@
+import Matrix from './';
+
+/**
+ *
+ * @param {Matrix} product
+ */
+export default function clone(product) {
+  let cloned = new Matrix();
+  cloned.rows = parseInt(product.rows);
+  cloned.columns = parseInt(product.columns);
+  cloned.weights = product.weights.slice(0);
+  cloned.recurrence = product.recurrence.slice(0);
+  return cloned;
+}
diff --git a/src/recurrent/matrix/copy.js b/src/recurrent/matrix/copy.js
new file mode 100644
index 000000000..23ec84027
--- /dev/null
+++ b/src/recurrent/matrix/copy.js
@@ -0,0 +1,11 @@
+/*
+ *
+ * @param {Matrix} product
+ * @param {Matrix} left
+ */
+export default function copy(product, left) {
+  product.rows = parseInt(left.rows);
+  product.columns = parseInt(left.columns);
+  product.weights = left.weights.slice(0);
+  product.recurrence = left.recurrence.slice(0);
+}
diff --git a/src/recurrent/matrix/equation.js b/src/recurrent/matrix/equation.js
new file mode 100644
index 000000000..376ac4a7d
--- /dev/null
+++ b/src/recurrent/matrix/equation.js
@@ -0,0 +1,270 @@
+import Matrix from './';
+import OnesMatrix from './ones-matrix';
+import copy from './copy';
+import cloneNegative from './clone-negative';
+import add from './add';
+import addB from './add-b';
+import allOnes from './all-ones';
+import multiply from './multiply';
+import multiplyB from './multiply-b';
+import multiplyElement from './multiply-element';
+import multiplyElementB from './multiply-element-b';
+import relu from './relu';
+import reluB from './relu-b';
+import rowPluck from './row-pluck';
+import rowPluckB from './row-pluck-b';
+import sigmoid from './sigmoid';
+import sigmoidB from './sigmoid-b';
+import tanh from './tanh';
+import tanhB from './tanh-b';
+
+export default class Equation {
+  constructor() {
+    this.inputRow = 0;
+    this.states = [];
+    this.previousResults = [];
+    this.previousResultInputs = [];
+    this.allMatrices = [];
+  }
+
+  /**
+   * connects two matrices together by add
+   * @param {Matrix} left
+   * @param {Matrix} right
+   * @returns {Matrix}
+   */
+  add(left, right) {
+    if (left.weights.length !== right.weights.length) {
+      throw new Error('misaligned matrices');
+    }
+    let product = new Matrix(left.rows, left.columns);
+    this.allMatrices.push(product);
+    this.states.push({
+      left: left,
+      right: right,
+      product: product,
+      forwardFn: add,
+      backpropagationFn: addB
+    });
+    return product;
+  }
+
+  /**
+   *
+   * @param {Number} rows
+   * @param {Number} columns
+   * @returns {Matrix}
+   */
+  allOnes(rows, columns) {
+    let product = new Matrix(rows, columns);
+    this.allMatrices.push(product);
+    this.states.push({
+      left: product,
+      product: product,
+      forwardFn: allOnes
+    });
+    return product;
+  }
+
+  /**
+   *
+   * @param {Matrix} m
+   * @returns {Matrix}
+   */
+  cloneNegative(m) {
+    let product = new Matrix(m.rows, m.columns);
+    this.allMatrices.push(product);
+    this.states.push({
+      left: m,
+      product: product,
+      forwardFn: cloneNegative
+    });
+    return product;
+  }
+
+  /**
+   * connects two matrices together by subtract
+   * @param {Matrix} left
+   * @param {Matrix} right
+   * @returns {Matrix}
+   */
+  subtract(left, right) {
+    if (left.weights.length !== right.weights.length) {
+      throw new Error('misaligned matrices');
+    }
+    return this.add(this.add(this.allOnes(left.rows, left.columns), this.cloneNegative(left)), right);
+  }
+
+  /**
+   * connects two matrices together by multiply
+   * @param {Matrix} left
+   * @param {Matrix} right
+   * @returns {Matrix}
+   */
+  multiply(left, right) {
+    if (left.columns !== right.rows) {
+      throw new Error('misaligned matrices');
+    }
+    let product = new Matrix(left.rows, right.columns);
+    this.allMatrices.push(product);
+    this.states.push({
+      left: left,
+      right: right,
+      product: product,
+      forwardFn: multiply,
+      backpropagationFn: multiplyB
+    });
+    return product;
+  }
+
+  /**
+   * connects two matrices together by multiplyElement
+   * @param {Matrix} left
+   * @param {Matrix} right
+   * @returns {Matrix}
+   */
+  multiplyElement(left, right) {
+    if (left.weights.length !== right.weights.length) {
+      throw new Error('misaligned matrices');
+    }
+    let product = new Matrix(left.rows, left.columns);
+    this.allMatrices.push(product);
+    this.states.push({
+      left: left,
+      right: right,
+      product: product,
+      forwardFn: multiplyElement,
+      backpropagationFn: multiplyElementB
+    });
+    return product;
+  }
+
+  /**
+   * connects a matrix to relu
+   * @param {Matrix} m
+   * @returns {Matrix}
+   */
+  relu(m) {
+    let product = new Matrix(m.rows, m.columns);
+    this.allMatrices.push(product);
+    this.states.push({
+      left: m,
+      product: product,
+      forwardFn: relu,
+      backpropagationFn: reluB
+    });
+    return product;
+  }
+
+  /**
+   * connects a matrix via a row
+   * @param {Matrix} m
+   * @returns {Matrix}
+   */
+  inputMatrixToRow(m) {
+    let self = this;
+    let product = new Matrix(m.columns, 1);
+    this.allMatrices.push(product);
+    this.states.push({
+      left: m,
+      get right () {
+        return self.inputRow;
+      },
+      product: product,
+      forwardFn: rowPluck,
+      backpropagationFn: rowPluckB
+    });
+    return product;
+  }
+
+  /**
+   * connects a matrix to sigmoid
+   * @param {Matrix} m
+   * @returns {Matrix}
+   */
+  sigmoid(m) {
+    let product = new Matrix(m.rows, m.columns);
+    this.allMatrices.push(product);
+    this.states.push({
+      left: m,
+      product: product,
+      forwardFn: sigmoid,
+      backpropagationFn: sigmoidB
+    });
+    return product;
+  }
+
+  /**
+   * connects a matrix to tanh
+   * @param {Matrix} m
+   * @returns {Matrix}
+   */
+  tanh(m) {
+    let product = new Matrix(m.rows, m.columns);
+    this.allMatrices.push(product);
+    this.states.push({
+      left: m,
+      product: product,
+      forwardFn: tanh,
+      backpropagationFn: tanhB
+    });
+    return product;
+  }
+
+  /**
+   *
+   * @param m
+   * @returns {Matrix}
+   */
+  observe(m) {
+    let iForward = 0;
+    let iBackpropagate = 0;
+    this.states.push({
+      forwardFn: function() {
+        iForward++;
+      },
+      backpropagationFn: function() {
+        iBackpropagate++;
+      }
+    });
+    return m;
+  }
+
+  /**
+   * @patam {Number} [rowIndex]
+   * @output {Matrix}
+   */
+  run(rowIndex = 0) {
+    this.inputRow = rowIndex;
+    let state;
+    for (let i = 0, max = this.states.length; i < max; i++) {
+      state = this.states[i];
+      if (!state.hasOwnProperty('forwardFn')) {
+        continue;
+      }
+      state.forwardFn(state.product, state.left, state.right);
+    }
+
+    return state.product;
+  }
+
+  /**
+   * @patam {Number} [rowIndex]
+   * @output {Matrix}
+   */
+  runBackpropagate(rowIndex = 0) {
+    this.inputRow = rowIndex;
+
+    let i = this.states.length;
+    let state;
+    while (i-- > 0) {
+      state = this.states[i];
+      if (!state.hasOwnProperty('backpropagationFn')) {
+        continue;
+      }
+      state.backpropagationFn(state.product, state.left, state.right);
+    }
+
+    return state.product;
+  }
+}
diff --git a/src/recurrent/matrix/index.js b/src/recurrent/matrix/index.js
new file mode 100644
index 000000000..8a576c123
--- /dev/null
+++ b/src/recurrent/matrix/index.js
@@ -0,0 +1,106 @@
+import zeros from '../../utilities/zeros';
+
+/**
+ * A matrix
+ * @param {Number} [rows]
+ * @param {Number} [columns]
+ * @constructor
+ */
+export default class Matrix {
+  constructor(rows, columns) {
+    if (typeof rows === 'undefined') return;
+    if (typeof columns === 'undefined') return;
+
+    this.rows = rows;
+    this.columns = columns;
+    this.weights = zeros(rows * columns);
+    this.recurrence = zeros(rows * columns);
+  }
+
+  /**
+   *
+   * @param {Number} row
+   * @param {Number} col
+   * @returns {Float64Array|Array}
+   */
+  getWeights(row, col) {
+    // slow but careful accessor function
+    // we want row-major order
+    let ix = (this.columns * row) + col;
+    if (ix < 0 && ix >= this.weights.length) throw new Error('get accessor is skewed');
+    return this.weights[ix];
+  }
+
+  /**
+   *
+   * @param {Number} row
+   * @param {Number} col
+   * @param v
+   * @returns {Matrix}
+   */
+  setWeight(row, col, v) {
+    // slow but careful accessor function
+    let ix = (this.columns * row) + col;
+    if (ix < 0 && ix >= this.weights.length) throw new Error('set accessor is skewed');
+    this.weights[ix] = v;
+  }
+
+  /**
+   *
+   * @param {Number} row
+   * @param {Number} col
+   * @param v
+   * @returns {Matrix}
+   */
+  setRecurrence(row, col, v) {
+    // slow but careful accessor function
+    let ix = (this.columns * row) + col;
+    if (ix < 0 && ix >= this.weights.length) throw new Error('set accessor is skewed');
+    this.recurrence[ix] = v;
+  }
+
+  /**
+   *
+   * @returns {{rows: *, columns: *, weights: Array}}
+   */
+  toJSON() {
+    return {
+      rows: this.rows,
+      columns: this.columns,
+      weights: this.weights.slice(0)
+    };
+  }
+
+  static fromJSON(json) {
+    let matrix = new Matrix(json.rows, json.columns);
+    for (let i = 0, max = json.rows * json.columns; i < max; i++) {
+      matrix.weights[i] = json.weights[i]; // copy over weights
+    }
+    return matrix;
+  }
+
+  /**
+   *
+   * @param weightRows
+   * @param [recurrenceRows]
+   * @returns {Matrix}
+   */
+  static fromArray(weightRows, recurrenceRows) {
+    var rows = weightRows.length;
+    var columns = weightRows[0].length;
+    var m = new Matrix(rows, columns);
+
+    recurrenceRows = recurrenceRows || weightRows;
+
+    for (var rowIndex = 0; rowIndex < rows; rowIndex++) {
+      var weightValues = weightRows[rowIndex];
+      var recurrentValues = recurrenceRows[rowIndex];
+      for (var columnIndex = 0; columnIndex < columns; columnIndex++) {
+        m.setWeight(rowIndex, columnIndex, weightValues[columnIndex]);
+        m.setRecurrence(rowIndex, columnIndex, recurrentValues[columnIndex]);
+      }
+    }
+
+    return m;
+  }
+}
diff --git a/src/recurrent/matrix/max-i.js b/src/recurrent/matrix/max-i.js
new file mode 100644
index 000000000..7df45c50b
--- /dev/null
+++ b/src/recurrent/matrix/max-i.js
@@ -0,0 +1,19 @@
+/**
+ *
+ * @param {Matrix} m
+ * @returns {number}
+ */
+export default function maxI(m) {
+  // argmax of array w
+  let w = m.weights;
+  let maxv = w[0];
+  let maxix = 0;
+  for (let i = 1, max = w.length; i < max; i++) {
+    let v = w[i];
+    if (v < maxv) continue;
+
+    maxix = i;
+    maxv = v;
+  }
+  return maxix;
+};
diff --git a/src/recurrent/matrix/multiply-b.js b/src/recurrent/matrix/multiply-b.js
new file mode 100644
index 000000000..52c3440d2
--- /dev/null
+++ b/src/recurrent/matrix/multiply-b.js
@@ -0,0 +1,26 @@
+/**
+ * multiplies {from} recurrence to {left} and {right}
+ * @param {Matrix} product
+ * @param {Matrix} left
+ * @param {Matrix} right
+ */
+export default function multiplyB(product, left, right) {
+  let leftRows = left.rows;
+  let leftColumns = left.columns;
+  let rightColumns = right.columns;
+
+  // loop over rows of left
+  for(let leftRow = 0; leftRow < leftRows; leftRow++) {
+
+    // loop over cols of right
+    for(let rightColumn = 0; rightColumn < rightColumns; rightColumn++) {
+
+      //loop over columns of left
+      for(let leftColumn = 0; leftColumn < leftColumns; leftColumn++) {
+        let backPropagateValue = product.recurrence[rightColumns * leftRow + rightColumn];
+        left.recurrence[leftColumns * leftRow + leftColumn] += right.weights[rightColumns * leftColumn + rightColumn] * backPropagateValue;
+        right.recurrence[rightColumns * leftColumn + rightColumn] += left.weights[leftColumns * leftRow + leftColumn] * backPropagateValue;
+      }
+    }
+  }
+}
diff --git a/src/recurrent/matrix/multiply-element-b.js b/src/recurrent/matrix/multiply-element-b.js
new file mode 100644
index 000000000..7b8315ef2
--- /dev/null
+++ b/src/recurrent/matrix/multiply-element-b.js
@@ -0,0 +1,12 @@
+/**
+ * multiplies {left} and {right} weight by {from} recurrence into {left} and {right} recurrence
+ * @param {Matrix} product
+ * @param {Matrix} left
+ * @param {Matrix} right
+ */
+export default function multiplyElementB(product, left, right) {
+  for(let i = 0, weights = left.weights.length; i < weights; i++) {
+    left.recurrence[i] += right.weights[i] * product.recurrence[i];
+    right.recurrence[i] += left.weights[i] * product.recurrence[i];
+  }
+}
diff --git a/src/recurrent/matrix/multiply-element.js b/src/recurrent/matrix/multiply-element.js
new file mode 100644
index 000000000..cc5db80b7
--- /dev/null
+++ b/src/recurrent/matrix/multiply-element.js
@@ -0,0 +1,10 @@
+/**
+ * @param {Matrix} product
+ * @param {Matrix} left
+ * @param {Matrix} right
+ */
+export default function multiplyElement(product, left, right) {
+  for(let i = 0, weights = left.weights.length; i < weights; i++) {
+    product.weights[i] = left.weights[i] * right.weights[i];
+  }
+}
diff --git a/src/recurrent/matrix/multiply.js b/src/recurrent/matrix/multiply.js
new file mode 100644
index 000000000..279ac3519
--- /dev/null
+++ b/src/recurrent/matrix/multiply.js
@@ -0,0 +1,31 @@
+/**
+ * multiply {left} and {right} matrix weights to {into}
+ * @param {Matrix} product
+ * @param {Matrix} left
+ * @param {Matrix} right
+ */
+export default function multiply(product, left, right) {
+  let leftRows = left.rows;
+  let leftColumns = left.columns;
+  let rightColumns = right.columns;
+
+  // loop over rows of left
+  for(let leftRow = 0; leftRow < leftRows; leftRow++) {
+
+    // loop over cols of right
+    for(let rightColumn = 0; rightColumn < rightColumns; rightColumn++) {
+
+      // dot product loop
+      let dot = 0;
+
+      //loop over columns of left
+      for(let leftColumn = 0; leftColumn < leftColumns; leftColumn++) {
+        dot +=
+            left.weights[leftColumns * leftRow + leftColumn]
+          * right.weights[rightColumns * leftColumn + rightColumn];
+      }
+      let i = rightColumns * leftRow + rightColumn;
+      product.weights[i] = dot;
+    }
+  }
+}
diff --git a/src/recurrent/matrix/ones-matrix.js b/src/recurrent/matrix/ones-matrix.js
new file mode 100644
index 000000000..017a561ff
--- /dev/null
+++ b/src/recurrent/matrix/ones-matrix.js
@@ -0,0 +1,17 @@
+import Matrix from './';
+import ones from '../../utilities/ones';
+
+/** return Matrix but filled with random numbers from gaussian
+ * @param {Number} [rows]
+ * @param {Number} [columns]
+ * @constructor
+ */
+export default class OnesMatrix extends Matrix {
+  constructor(rows, columns) {
+    super(rows, columns);
+    this.rows = rows;
+    this.columns = columns;
+    this.weights = ones(rows * columns);
+    this.recurrence = ones(rows * columns);
+  }
+}
diff --git a/src/recurrent/matrix/random-matrix.js b/src/recurrent/matrix/random-matrix.js
new file mode 100644
index 000000000..5fb069c67
--- /dev/null
+++ b/src/recurrent/matrix/random-matrix.js
@@ -0,0 +1,20 @@
+import Matrix from './';
+import { randomF } from '../../utilities/random';
+
+/** return Matrix but filled with random numbers from gaussian
+ * @param {Number} [rows]
+ * @param {Number} [columns]
+ * @param std
+ * @constructor
+ */
+export default class RandomMatrix extends Matrix {
+  constructor(rows, columns, std) {
+    super(rows, columns);
+    this.rows = rows;
+    this.columns = columns;
+    this.std = std;
+    for(let i = 0, max = this.weights.length; i < max; i++) {
+      this.weights[i] = randomF(-std, std);
+    }
+  }
+}
diff --git a/src/recurrent/matrix/random-n-matrix.js b/src/recurrent/matrix/random-n-matrix.js
new file mode 100644
index 000000000..f62bf261b
--- /dev/null
+++ b/src/recurrent/matrix/random-n-matrix.js
@@ -0,0 +1,22 @@
+import Matrix from './';
+import { randomN } from '../../utilities/random';
+/**
+ *
+ * @param {Number} rows
+ * @param {Number} columns
+ * @param mu
+ * @param std
+ * @constructor
+ */
+export default class extends Matrix {
+  constructor(rows, columns, mu, std) {
+    super(rows, columns);
+    this.fillRandN(mu, std);
+  }
+  // fill matrix with random gaussian numbers
+  fillRandN(mu, std) {
+    for(let i = 0, max = this.weights.length; i < max; i++) {
+      this.weights[i] = randomN(mu, std);
+    }
+  }
+}
diff --git a/src/recurrent/matrix/relu-b.js b/src/recurrent/matrix/relu-b.js
new file mode 100644
index 000000000..7fbba4408
--- /dev/null
+++ b/src/recurrent/matrix/relu-b.js
@@ -0,0 +1,10 @@
+/**
+ * adds {from} recurrence to {m} recurrence when {m} weights are above other a threshold of 0
+ * @param {Matrix} product
+ * @param {Matrix} m
+ */
+export default function reluB(product, left) {
+  for(let i = 0, max = product.recurrence.length; i < max; i++) {
+    left.recurrence[i] += left.weights[i] > 0 ? product.recurrence[i] : 0;
+  }
+}
diff --git a/src/recurrent/matrix/relu.js b/src/recurrent/matrix/relu.js
new file mode 100644
index 000000000..40747075e
--- /dev/null
+++ b/src/recurrent/matrix/relu.js
@@ -0,0 +1,11 @@
+/**
+ *
+ * relu {m} weights to {into} weights
+ * @param {Matrix} product
+ * @param {Matrix} left
+ */
+export default function relu(product, left) {
+  for(let i = 0, max = left.weights.length; i < max; i++) {
+    product.weights[i] = Math.max(0, left.weights[i]); // relu
+  }
+}
diff --git a/src/recurrent/matrix/row-pluck-b.js b/src/recurrent/matrix/row-pluck-b.js
new file mode 100644
index 000000000..59fa3edcf
--- /dev/null
+++ b/src/recurrent/matrix/row-pluck-b.js
@@ -0,0 +1,11 @@
+/**
+ * adds {from} recurrence into {m} recurrence
+ * @param {Matrix} product
+ * @param {Matrix} left
+ * @param {Number} rowIndex
+ */
+export default function rowPluckB(product, left, rowIndex) {
+  for (let column = 0, columns = left.columns; column < columns; column++) {
+    left.recurrence[columns * rowIndex + column] += product.recurrence[column];
+  }
+}
diff --git a/src/recurrent/matrix/row-pluck.js b/src/recurrent/matrix/row-pluck.js
new file mode 100644
index 000000000..9b19e624e
--- /dev/null
+++ b/src/recurrent/matrix/row-pluck.js
@@ -0,0 +1,10 @@
+/**
+ * @param {Matrix} product
+ * @param {Matrix} left
+ * @param {Number} rowPluckIndex
+ */
+export default function rowPluck(product, left, rowPluckIndex) {
+  for (let column = 0, columns = left.columns; column < columns; column++) {
+    product.weights[column] = left.weights[columns * rowPluckIndex + column];
+  }
+}
diff --git a/src/recurrent/matrix/sample-i.js b/src/recurrent/matrix/sample-i.js
new file mode 100644
index 000000000..7597ba000
--- /dev/null
+++ b/src/recurrent/matrix/sample-i.js
@@ -0,0 +1,30 @@
+import { randomF as _randomF } from '../../utilities/random';
+
+//prevent parser from renaming when calling toString() method later
+const randomF = _randomF;
+/**
+ *
+ * @param {Matrix} m
+ * @returns {number}
+ */
+export default function sampleI(m) {
+  // sample argmax from w, assuming w are
+  // probabilities that sum to one
+  let r = randomF(0, 1);
+  let x = 0;
+  let i = 0;
+  let w = m.weights;
+
+  //TODO: Needed?
+  if (isNaN(w[0])) {
+    throw new Error('NaN');
+  }
+
+  while (true) {
+    x += w[i];
+    if(x > r) {
+      return i;
+    }
+    i++;
+  }
+}
\ No newline at end of file
diff --git a/src/recurrent/matrix/sigmoid-b.js b/src/recurrent/matrix/sigmoid-b.js
new file mode 100644
index 000000000..e93bb5508
--- /dev/null
+++ b/src/recurrent/matrix/sigmoid-b.js
@@ -0,0 +1,11 @@
+/**
+ *
+ * @param {Matrix} product
+ * @param {Matrix} left
+ */
+export default function sigmoidB(product, left) {
+  for(let i = 0, max = product.recurrence.length; i < max; i++) {
+    let mwi = product.weights[i];
+    left.recurrence[i] += mwi * (1 - mwi) * product.recurrence[i];
+  }
+}
diff --git a/src/recurrent/matrix/sigmoid.js b/src/recurrent/matrix/sigmoid.js
new file mode 100644
index 000000000..ba33e353d
--- /dev/null
+++ b/src/recurrent/matrix/sigmoid.js
@@ -0,0 +1,16 @@
+/**
+ * @param {Matrix} product
+ * @param {Matrix} left
+ */
+export default function sigmoid(product, left) {
+  // sigmoid nonlinearity
+  for(let i=0, max = left.weights.length; i < max; i++) {
+    product.weights[i] = 1 / ( 1 + Math.exp(-left.weights[i]));
+  }
+}
+
+
+function sig(x) {
+  // helper function for computing sigmoid
+  return 1 / (1 + Math.exp(-x));
+}
\ No newline at end of file
diff --git a/src/recurrent/matrix/softmax.js b/src/recurrent/matrix/softmax.js
new file mode 100644
index 000000000..3a91f783d
--- /dev/null
+++ b/src/recurrent/matrix/softmax.js
@@ -0,0 +1,36 @@
+import _Matrix from './';
+
+//prevent parser from renaming when calling toString() method later
+const Matrix = _Matrix;
+/**
+ *
+ * @param {Matrix} m
+ * @returns {Matrix}
+ */
+export default function softmax(m) {
+  let result = new Matrix(m.rows, m.columns); // probability volume
+  let maxVal = -999999;
+  let i;
+  let max = m.weights.length;
+
+  for (i = 0; i < max; i++) {
+    if(m.weights[i] > maxVal) {
+      maxVal = m.weights[i];
+    }
+  }
+
+  let s = 0;
+  for (i = 0; i < max; i++) {
+    result.weights[i] = Math.exp(m.weights[i] - maxVal);
+    s += result.weights[i];
+  }
+
+  for (i = 0; i < max; i++) {
+    result.weights[i] /= s;
+  }
+
+  // no backward pass here needed
+  // since we will use the computed probabilities outside
+  // to set gradients directly on m
+  return result;
+}
diff --git a/src/recurrent/matrix/tanh-b.js b/src/recurrent/matrix/tanh-b.js
new file mode 100644
index 000000000..07d3176e2
--- /dev/null
+++ b/src/recurrent/matrix/tanh-b.js
@@ -0,0 +1,12 @@
+/**
+ *
+ * @param {Matrix} product
+ * @param {Matrix} left
+ */
+export default function tanhB(product, left) {
+  for(let i = 0, max = product.recurrence.length; i < max; i++) {
+    // grad for z = tanh(x) is (1 - z^2)
+    let mwi = product.weights[i];
+    left.recurrence[i] += (1 - mwi * mwi) * product.recurrence[i];
+  }
+}
diff --git a/src/recurrent/matrix/tanh.js b/src/recurrent/matrix/tanh.js
new file mode 100644
index 000000000..5444da3d0
--- /dev/null
+++ b/src/recurrent/matrix/tanh.js
@@ -0,0 +1,10 @@
+/**
+ * @param {Matrix} product
+ * @param {Matrix} left
+ */
+export default function tanh(product, left) {
+  // tanh nonlinearity
+  for(let i = 0, max = left.weights.length; i < max; i++) {
+    product.weights[i] = Math.tanh(left.weights[i]);
+  }
+}
diff --git a/src/recurrent/rnn.js b/src/recurrent/rnn.js
new file mode 100644
index 000000000..1196af8d2
--- /dev/null
+++ b/src/recurrent/rnn.js
@@ -0,0 +1,708 @@
+import Matrix from './matrix';
+import RandomMatrix from './matrix/random-matrix';
+import Equation from './matrix/equation';
+import sampleI from './matrix/sample-i';
+import maxI from './matrix/max-i';
+import softmax from './matrix/softmax';
+import copy from './matrix/copy';
+import { randomF } from '../utilities/random';
+import zeros from '../utilities/zeros';
+
+const defaults = {
+  isBackPropagate: true,
+  // hidden size should be a list
+  inputSize: 20,
+  inputRange: 20,
+  hiddenSizes:[20,20],
+  outputSize: 20,
+  learningRate: 0.01,
+  decayRate: 0.999,
+  smoothEps: 1e-8,
+  regc: 0.000001,
+  clipval: 5,
+  json: null
+};
+
+export default class RNN {
+  constructor(options) {
+    options = options || {};
+
+    for (let p in defaults) {
+      if (defaults.hasOwnProperty(p) && p !== 'isBackPropagate') {
+        this[p] = options.hasOwnProperty(p) ? options[p] : defaults[p];
+      }
+    }
+
+    this.stepCache = {};
+    this.runs = 0;
+    this.totalPerplexity = null;
+    this.totalCost = null;
+    this.ratioClipped = null;
+
+    this.model = {
+      input: null,
+      hiddenLayers: [],
+      output: null,
+      equations: [],
+      allMatrices: [],
+      outputMatrixIndex: -1,
+      equationConnections: []
+    };
+
+    if (this.json) {
+      this.fromJSON(this.json);
+    } else {
+      this.mapModel();
+    }
+  }
+
+  createHiddenLayers() {
+    let hiddenSizes = this.hiddenSizes;
+    let model = this.model;
+    let hiddenLayers = model.hiddenLayers;
+    //0 is end, so add 1 to offset
+    hiddenLayers.push(this.getModel(hiddenSizes[0], this.inputSize));
+    let prevSize = hiddenSizes[0];
+
+    for (let d = 1; d < hiddenSizes.length; d++) { // loop over depths
+      let hiddenSize = hiddenSizes[d];
+      hiddenLayers.push(this.getModel(hiddenSize, prevSize));
+      prevSize = hiddenSize;
+    }
+  }
+
+  /**
+   *
+   * @param {Number} hiddenSize
+   * @param {Number} prevSize
+   * @returns {object}
+   */
+  getModel(hiddenSize, prevSize) {
+    return {
+      //wxh
+      weight: new RandomMatrix(hiddenSize, prevSize, 0.08),
+      //whh
+      transition: new RandomMatrix(hiddenSize, hiddenSize, 0.08),
+      //bhh
+      bias: new Matrix(hiddenSize, 1)
+    };
+  }
+
+  /**
+   *
+   * @param {Equation} equation
+   * @param {Matrix} inputMatrix
+   * @param {Matrix} previousResult
+   * @param {Object} hiddenLayer
+   * @returns {Matrix}
+   */
+  getEquation(equation, inputMatrix, previousResult, hiddenLayer) {
+    let relu = equation.relu.bind(equation);
+    let add = equation.add.bind(equation);
+    let multiply = equation.multiply.bind(equation);
+
+    return relu(
+      add(
+        add(
+          multiply(
+            hiddenLayer.weight,
+            inputMatrix
+          ),
+          multiply(
+            hiddenLayer.transition,
+            previousResult
+          )
+        ),
+        hiddenLayer.bias
+      )
+    );
+  }
+
+  createInputMatrix() {
+    //0 is end, so add 1 to offset
+    this.model.input = new RandomMatrix(this.inputRange + 1, this.inputSize, 0.08);
+  }
+
+  createOutputMatrix() {
+    let model = this.model;
+    let outputSize = this.outputSize;
+    let lastHiddenSize = this.hiddenSizes[this.hiddenSizes.length - 1];
+
+    //0 is end, so add 1 to offset
+    //whd
+    model.outputConnector = new RandomMatrix(outputSize + 1, lastHiddenSize, 0.08);
+    //0 is end, so add 1 to offset
+    //bd
+    model.output = new Matrix(outputSize + 1, 1);
+  }
+
+  bindEquation() {
+    let model = this.model;
+    let hiddenSizes = this.hiddenSizes;
+    let hiddenLayers = model.hiddenLayers;
+    let equation = new Equation();
+    let outputs = [];
+    let equationConnection = model.equationConnections.length > 0
+      ? model.equationConnections[model.equationConnections.length - 1]
+      : hiddenSizes.map((size) => new Matrix(hiddenSizes[0], 1))
+      ;
+
+      // 0 index
+    let output = this.getEquation(equation, equation.inputMatrixToRow(model.input), equationConnection[0], hiddenLayers[0]);
+    outputs.push(output);
+    // 1+ indexes
+    for (let i = 1, max = hiddenSizes.length; i < max; i++) {
+      output = this.getEquation(equation, output, equationConnection[i], hiddenLayers[i]);
+      outputs.push(output);
+    }
+
+    model.equationConnections.push(outputs);
+    equation.add(equation.multiply(model.outputConnector, output), model.output);
+    model.allMatrices = model.allMatrices.concat(equation.allMatrices);
+    model.equations.push(equation);
+  }
+
+  mapModel() {
+    let model = this.model;
+    let hiddenLayers = model.hiddenLayers;
+    let allMatrices = model.allMatrices;
+
+    this.createInputMatrix();
+    if (!model.input) throw new Error('net.model.input not set');
+    allMatrices.push(model.input);
+
+    this.createHiddenLayers();
+    if (!model.hiddenLayers.length) throw new Error('net.hiddenLayers not set');
+    for (let i = 0, max = hiddenLayers.length; i < max; i++) {
+      let hiddenMatrix = hiddenLayers[i];
+      for (let property in hiddenMatrix) {
+        if (!hiddenMatrix.hasOwnProperty(property)) continue;
+        allMatrices.push(hiddenMatrix[property]);
+      }
+    }
+
+    this.createOutputMatrix();
+    if (!model.outputConnector) throw new Error('net.model.outputConnector not set');
+    if (!model.output) throw new Error('net.model.output not set');
+
+    allMatrices.push(model.outputConnector);
+    model.outputMatrixIndex = allMatrices.length;
+    allMatrices.push(model.output);
+  }
+
+  run(input) {
+    this.train(input);
+    this.runBackpropagate(input);
+    this.step();
+  }
+
+  train(input) {
+    this.runs++;
+    let model = this.model;
+    let max = input.length;
+    let log2ppl = 0;
+    let cost = 0;
+
+    let equation;
+    while (model.equations.length <= input.length + 1) {//first and last are zeros
+      this.bindEquation();
+    }
+    for (let inputIndex = -1, inputMax = input.length; inputIndex < inputMax; inputIndex++) {
+      // start and end tokens are zeros
+      equation = model.equations[inputIndex + 1];
+
+      let source = (inputIndex === -1 ? 0 : input[inputIndex] + 1); // first step: start with START token
+      let target = (inputIndex === max - 1 ? 0 : input[inputIndex + 1] + 1); // last step: end with END token
+      let output = equation.run(source);
+      // set gradients into log probabilities
+      let logProbabilities = output; // interpret output as log probabilities
+      let probabilities = softmax(output); // compute the softmax probabilities
+
+      log2ppl += -Math.log2(probabilities.weights[target]); // accumulate base 2 log prob and do smoothing
+      cost += -Math.log(probabilities.weights[target]);
+
+      // write gradients into log probabilities
+      logProbabilities.recurrence = probabilities.weights;
+      logProbabilities.recurrence[target] -= 1;
+    }
+
+    this.totalPerplexity = Math.pow(2, log2ppl / (max - 1));
+    this.totalCost = cost;
+  }
+
+  runBackpropagate(input) {
+    var i = input.length + 0;
+    var model = this.model;
+    var equations = model.equations;
+    while(i > 0) {
+      equations[i].runBackpropagate(input[i - 1] + 1);
+      i--;
+    }
+    equations[0].runBackpropagate(0);
+  }
+
+  step() {
+    // perform parameter update
+    let stepSize = this.learningRate;
+    let regc = this.regc;
+    let clipval = this.clipval;
+    let model = this.model;
+    let numClipped = 0;
+    let numTot = 0;
+    let allMatrices = model.allMatrices;
+    let matrixIndexes = allMatrices.length;
+    for (let matrixIndex = 0; matrixIndex < matrixIndexes; matrixIndex++) {
+      let matrix = allMatrices[matrixIndex];
+      if (!(matrixIndex in this.stepCache)) {
+        this.stepCache[matrixIndex] = new Matrix(matrix.rows, matrix.columns);
+      }
+      let cache = this.stepCache[matrixIndex];
+
+      //if we are in an equation, reset the weights and recurrence to 0, to prevent exploding gradient problem
+      if (matrixIndex > model.outputMatrixIndex) {
+        for (let i = 0, n = matrix.weights.length; i < n; i++) {
+          matrix.weights[i] = 0;
+          matrix.recurrence[i] = 0;
+        }
+        continue;
+      }
+
+      for (let i = 0, n = matrix.weights.length; i < n; i++) {
+        // rmsprop adaptive learning rate
+        let mdwi = matrix.recurrence[i];
+        cache.weights[i] = cache.weights[i] * this.decayRate + (1 - this.decayRate) * mdwi * mdwi;
+        // gradient clip
+        if (mdwi > clipval) {
+          mdwi = clipval;
+          numClipped++;
+        }
+        if (mdwi < -clipval) {
+          mdwi = -clipval;
+          numClipped++;
+        }
+        numTot++;
+
+        // update (and regularize)
+        matrix.weights[i] = matrix.weights[i] + -stepSize * mdwi / Math.sqrt(cache.weights[i] + this.smoothEps) - regc * matrix.weights[i];
+        matrix.recurrence[i] = 0; // reset gradients for next iteration
+      }
+    }
+    this.ratioClipped = numClipped / numTot;
+  }
+
+  predict(result = [], maxPredictionLength = 100, _sampleI = false, temperature = 1) {
+    let model = this.model;
+    let equation;
+    let i = 0;
+    while (model.equations.length < maxPredictionLength) {
+      this.bindEquation();
+    }
+    while (true) {
+      let ix = result.length === 0 ? 0 : result[result.length - 1];
+      equation = model.equations[i];
+      // sample predicted letter
+      let output = equation.run(ix);
+
+      let logProbabilities = new Matrix(model.output.rows, model.output.columns);
+      copy(logProbabilities, output);
+      if (temperature !== 1 && _sampleI) {
+        // scale log probabilities by temperature and renormalize
+        // if temperature is high, logprobs will go towards zero
+        // and the softmax outputs will be more diffuse. if temperature is
+        // very low, the softmax outputs will be more peaky
+        for (let q = 0, nq = logProbabilities.weights.length; q < nq; q++) {
+          logProbabilities.weights[q] /= temperature;
+        }
+      }
+
+      let probs = softmax(logProbabilities);
+
+      if (_sampleI) {
+        ix = sampleI(probs);
+      } else {
+        ix = maxI(probs);
+      }
+
+      i++;
+      if (ix === 0) {
+        // END token predicted, break out
+        break;
+      }
+      if (i >= maxPredictionLength) {
+        // something is wrong
+        break;
+      }
+
+      result.push(ix);
+    }
+
+    return result.map((value) => value - 1);
+  }
+
+  /**
+   *
+   * @param input
+   * @returns {*}
+   */
+  runInput(input) {
+    this.outputs[0] = input;  // set output state of input layer
+
+    let output = null;
+    for (let layer = 1; layer <= this.outputLayer; layer++) {
+      for (let node = 0; node < this.sizes[layer]; node++) {
+        let weights = this.weights[layer][node];
+
+        let sum = this.biases[layer][node];
+        for (let k = 0; k < weights.length; k++) {
+          sum += weights[k] * input[k];
+        }
+        this.outputs[layer][node] = 1 / (1 + Math.exp(-sum));
+      }
+      output = input = this.outputs[layer];
+    }
+    return output;
+  }
+
+  /**
+   *
+   * @param data
+   * @param options
+   * @returns {{error: number, iterations: number}}
+   */
+  /*train(data, options) {
+    throw new Error('not yet implemented');
+    //data = this.formatData(data);
+
+    options = options || {};
+    let iterations = options.iterations || 20000;
+    let errorThresh = options.errorThresh || 0.005;
+    let log = options.log ? (typeof options.log === 'function' ? options.log : console.log) : false;
+    let logPeriod = options.logPeriod || 10;
+    let learningRate = options.learningRate || this.learningRate || 0.3;
+    let callback = options.callback;
+    let callbackPeriod = options.callbackPeriod || 10;
+    let sizes = [];
+    let inputSize = data[0].input.length;
+    let outputSize = data[0].output.length;
+    let hiddenSizes = this.hiddenSizes;
+    if (!hiddenSizes) {
+      sizes.push(Math.max(3, Math.floor(inputSize / 2)));
+    } else {
+      hiddenSizes.forEach(function(size) {
+        sizes.push(size);
+      });
+    }
+
+    sizes.unshift(inputSize);
+    sizes.push(outputSize);
+
+    //this.initialize(sizes, options.keepNetworkIntact);
+
+    let error = 1;
+    for (let i = 0; i < iterations && error > errorThresh; i++) {
+      let sum = 0;
+      for (let j = 0; j < data.length; j++) {
+        let err = this.trainPattern(data[j].input, data[j].output, learningRate);
+        sum += err;
+      }
+      error = sum / data.length;
+
+      if (log && (i % logPeriod == 0)) {
+        log('iterations:', i, 'training error:', error);
+      }
+      if (callback && (i % callbackPeriod == 0)) {
+        callback({ error: error, iterations: i });
+      }
+    }
+
+    return {
+      error: error,
+      iterations: i
+    };
+  }*/
+
+  /**
+   *
+   * @param input
+   * @param target
+   * @param learningRate
+   */
+  trainPattern(input, target, learningRate) {
+    throw new Error('not yet implemented');
+  }
+
+  /**
+   *
+   * @param target
+   */
+  calculateDeltas(target) {
+    throw new Error('not yet implemented');
+  }
+
+  /**
+   *
+   * @param learningRate
+   */
+  adjustWeights(learningRate) {
+    throw new Error('not yet implemented');
+  }
+
+  /**
+   *
+   * @param data
+   * @returns {*}
+   */
+  formatData(data) {
+    throw new Error('not yet implemented');
+  }
+
+  /**
+   *
+   * @param data
+   * @returns {
+   *  {
+   *    error: number,
+   *    misclasses: Array
+   *  }
+   * }
+   */
+  test(data) {
+    throw new Error('not yet implemented');
+  }
+
+  toJSON() {
+    let model = this.model;
+    let options = {};
+    for (let p in defaults) {
+      options[p] = this[p];
+    }
+
+    return {
+      type: this.constructor.name,
+      options: options,
+      input: model.input.toJSON(),
+      hiddenLayers: model.hiddenLayers.map((hiddenLayer) => {
+        let layers = {};
+        for (let p in hiddenLayer) {
+          layers[p] = hiddenLayer[p].toJSON();
+        }
+        return layers;
+      }),
+      outputConnector: this.model.outputConnector.toJSON(),
+      output: this.model.output.toJSON()
+    };
+  }
+
+  fromJSON(json) {
+    this.json = json;
+    let model = this.model;
+    let options = json.options;
+    let allMatrices = model.allMatrices;
+    model.input = Matrix.fromJSON(json.input);
+    allMatrices.push(model.input);
+    model.hiddenLayers = json.hiddenLayers.map((hiddenLayer) => {
+      let layers = {};
+      for (let p in hiddenLayer) {
+        layers[p] = Matrix.fromJSON(hiddenLayer[p]);
+        allMatrices.push(layers[p]);
+      }
+      return layers;
+    });
+    model.outputConnector = Matrix.fromJSON(json.outputConnector);
+    model.output = Matrix.fromJSON(json.output);
+    allMatrices.push(model.outputConnector, model.output);
+
+    for (let p in defaults) {
+      if (defaults.hasOwnProperty(p) && p !== 'isBackPropagate') {
+        this[p] = options.hasOwnProperty(p) ? options[p] : defaults[p];
+      }
+    }
+
+    this.bindEquation();
+  }
+
+  /**
+   *
+   * @returns {Function}
+   */
+  toFunction() {
+    let model = this.model;
+    let equations = this.model.equations;
+    let equation = equations[1];
+    let states = equation.states;
+    let modelAsString = JSON.stringify(this.toJSON());
+
+    function matrixOrigin(m, stateIndex) {
+      for (let i = 0, max = states.length; i < max; i++) {
+        let state = states[i];
+
+        if (i === stateIndex) {
+          let j = previousConnectionIndex(m);
+          switch (m) {
+            case state.left:
+              if (j > -1) {
+                return `typeof prevStates[${ j }] === 'object' ? prevStates[${ j }].product : new Matrix(${ m.rows }, ${ m.columns })`;
+              }
+            case state.right:
+              if (j > -1) {
+                return `typeof prevStates[${ j }] === 'object' ? prevStates[${ j }].product : new Matrix(${ m.rows }, ${ m.columns })`;
+              }
+            case state.product:
+              return `new Matrix(${ m.rows }, ${ m.columns })`;
+            default:
+              throw Error('unknown state');
+          }
+        }
+
+        if (m === state.product) return `states[${ i }].product`;
+        if (m === state.right) return `states[${ i }].right`;
+        if (m === state.left) return `states[${ i }].left`;
+      }
+    }
+
+    function previousConnectionIndex(m) {
+      const connection = model.equationConnections[0];
+      const states = equations[0].states;
+      for (let i = 0, max = states.length; i < max; i++) {
+        if (states[i].product === m) {
+          return i;
+        }
+      }
+      return connection.indexOf(m);
+    }
+
+    function matrixToString(m, stateIndex) {
+      if (!m || !m.rows || !m.columns) return 'null';
+
+      if (m === model.input) return `model.input`;
+      if (m === model.outputConnector) return `model.outputConnector`;
+      if (m === model.output) return `model.output`;
+
+      for (let i = 0, max = model.hiddenLayers.length; i < max; i++) {
+        let hiddenLayer = model.hiddenLayers[i];
+        for (let p in hiddenLayer) {
+          if (!hiddenLayer.hasOwnProperty(p)) continue;
+          if (hiddenLayer[p] !== m) continue;
+          return `model.hiddenLayers[${ i }].${ p }`;
+        }
+      }
+
+      return matrixOrigin(m, stateIndex);
+    }
+
+    function toInner(fnString) {
+      //crude, but should be sufficient for now
+      // function() { body }
+      fnString = fnString.toString().split('{');
+      fnString.shift();
+      // body }
+      fnString = fnString.join('{');
+      fnString = fnString.split('}');
+      fnString.pop();
+      // body
+      return fnString.join('}').split('\n').join('\n        ');
+    }
+
+    function fileName(fnName) {
+      return `src/recurrent/matrix/${ fnName.replace(/[A-Z]/g, function(value) { return '-' + value.toLowerCase(); }) }.js`;
+    }
+
+    let statesRaw = [];
+    let usedFunctionNames = {};
+    let innerFunctionsSwitch = [];
+    for (let i = 0, max = states.length; i < max; i++) {
+      let state = states[i];
+      statesRaw.push(`states[${ i }] = {
+      name: '${ state.forwardFn.name }',
+      left: ${ matrixToString(state.left, i) },
+      right: ${ matrixToString(state.right, i) },
+      product: ${ matrixToString(state.product, i) }
+    }`);
+
+      let fnName = state.forwardFn.name;
+      if (!usedFunctionNames[fnName]) {
+        usedFunctionNames[fnName] = true;
+        innerFunctionsSwitch.push(
+          `        case '${ fnName }': //compiled from ${ fileName(fnName) }
+          ${ toInner(state.forwardFn.toString()) }
+          break;`
+        );
+      }
+    }
+
+    return new Function('input', 'maxPredictionLength', '_sampleI', 'temperature', `
+  if (typeof input === 'undefined') input = [];
+  if (typeof maxPredictionLength === 'undefined') maxPredictionLength = 100;
+  if (typeof _sampleI === 'undefined') _sampleI = false;
+  if (typeof temperature === 'undefined') temperature = 1;
+  
+  var model = ${ modelAsString };
+  var _i = 0;
+  var result = input.slice(0);
+  var states = [];
+  var prevStates;
+  while (true) {
+    // sample predicted letter
+    var ix = result.length === 0 ? 0 : result[result.length - 1]; // first step: start with START token
+    var rowPluckIndex = ix; //connect up to rowPluck
+    prevStates = states;
+    states = [];
+    ${ statesRaw.join(';\n    ') };
+    for (var stateIndex = 0, stateMax = ${ statesRaw.length }; stateIndex < stateMax; stateIndex++) {
+      var state = states[stateIndex];
+      var product = state.product;
+      var left = state.left;
+      var right = state.right;
+      
+      switch (state.name) {
+${ innerFunctionsSwitch.join('\n') }
+      }
+    }
+    
+    var logProbabilities = state.product;
+    if (temperature !== 1 && _sampleI) {
+      // scale log probabilities by temperature and renormalize
+      // if temperature is high, logprobs will go towards zero
+      // and the softmax outputs will be more diffuse. if temperature is
+      // very low, the softmax outputs will be more peaky
+      for (var q = 0, nq = logProbabilities.weights.length; q < nq; q++) {
+        logProbabilities.weights[q] /= temperature;
+      }
+    }
+
+    var probs = softmax(logProbabilities);
+
+    if (_sampleI) {
+      ix = sampleI(probs);
+    } else {
+      ix = maxI(probs);
+    }
+    
+    _i++;
+    if (ix === 0) {
+      // END token predicted, break out
+      break;
+    }
+    if (_i >= maxPredictionLength) {
+      // something is wrong
+      break;
+    }
+
+    result.push(ix);
+  }
+
+  return result.map(function(value) { return value - 1; });
+  
+  function Matrix(rows, columns) {
+    this.rows = rows;
+    this.columns = columns;
+    this.weights = zeros(rows * columns);
+    this.recurrence = zeros(rows * columns);
+  }
+  ${ zeros.toString() }
+  ${ softmax.toString() }
+  ${ randomF.toString() }
+  ${ sampleI.toString() }
+  ${ maxI.toString() }`);
+  }
+}
diff --git a/src/utilities/ones.js b/src/utilities/ones.js
new file mode 100644
index 000000000..65457448c
--- /dev/null
+++ b/src/utilities/ones.js
@@ -0,0 +1,8 @@
+export default function ones(size) {
+  if (typeof Float64Array !== 'undefined') return new Float64Array(size).fill(1);
+  let array = new Array(size);
+  for (let i = 0; i < size; i++) {
+    array[i] = i;
+  }
+  return array;
+}
diff --git a/src/utilities/random.js b/src/utilities/random.js
new file mode 100644
index 000000000..0c03e225f
--- /dev/null
+++ b/src/utilities/random.js
@@ -0,0 +1,31 @@
+export function randomF(a, b) {
+  return Math.random() * (b - a) + a;
+}
+
+export function randomI(a, b) {
+  return Math.floor(Math.random() * (b - a) + a);
+}
+
+export function randomN(mu, std) {
+  return mu + gaussRandom() * std;
+}
+
+// Random numbers utils
+function gaussRandom() {
+  if (gaussRandom.returnV) {
+    gaussRandom.returnV = false;
+    return gaussRandom.vVal;
+  }
+  let u = 2 * Math.random() - 1;
+  let v = 2 * Math.random() - 1;
+  let r = u * u + v * v;
+  if (r == 0 || r > 1) {
+    return gaussRandom();
+  }
+  let c = Math.sqrt(-2 * Math.log(r) / r);
+  gaussRandom.vVal = v * c; // cache this
+  gaussRandom.returnV = true;
+  return u * c;
+}
+gaussRandom.returnV = false;
+gaussRandom.vVal = 0;
diff --git a/src/utilities/vocab.js b/src/utilities/vocab.js
new file mode 100644
index 000000000..cf1d60105
--- /dev/null
+++ b/src/utilities/vocab.js
@@ -0,0 +1,86 @@
+/**
+ *
+ * @param {String[]|Number[]} values
+ * @param maxThreshold
+ * @constructor
+ */
+export default class Vocab {
+  constructor(values, maxThreshold = 0) {
+    this.values = values;
+    // go over all characters and keep track of all unique ones seen
+    // count up all characters
+    this.indexTable = {};
+    this.characterTable = {};
+    this.characters = [];
+    let tempCharactersTable = {};
+    for (let vocabIndex = 0, vocabLength = values.length; vocabIndex < vocabLength; vocabIndex++) {
+      var characters = values[vocabIndex].toString();
+      for (let characterIndex = 0, charactersLength = characters.length; characterIndex < charactersLength; characterIndex++) {
+        let character = characters[characterIndex];
+        if (character in tempCharactersTable) continue;
+        tempCharactersTable[character] = true;
+        this.characters.push(character);
+      }
+    }
+
+    // filter by count threshold and create pointers
+
+    // NOTE: start at one because we will have START and END tokens!
+    // that is, START token will be index 0 in model letter vectors
+    // and END token will be index 0 in the next character softmax
+    let charactersLength = this.characters.length;
+    for(let characterIndex = 0; characterIndex < charactersLength; characterIndex++) {
+      let character = this.characters[characterIndex];
+      if(characterIndex >= maxThreshold) {
+        // add character to vocab
+        this.indexTable[character] = characterIndex;
+        this.characterTable[characterIndex] = character;
+      }
+    }
+  }
+
+  toIndexes(phrase, maxThreshold = 0) {
+    let result = [];
+    let indexTable = this.indexTable;
+
+    for (let i = 0, max = phrase.length; i < max; i++) {
+      let character = phrase[i];
+      let index = indexTable[character];
+      if (index < maxThreshold) continue;
+      result.push(index);
+    }
+
+    return result;
+  }
+
+  toCharacters(indexes, maxThreshold = 0) {
+    let result = [];
+    let characterTable = this.characterTable;
+
+    for (let i = 0, max = indexes.length; i < max; i++) {
+      let index = indexes[i];
+      if (index < maxThreshold) continue;
+      let character = characterTable[index];
+      result.push(character);
+    }
+
+    return result;
+  }
+
+  toString(indexes, maxThreshold) {
+    return this.toCharacters(indexes, maxThreshold).join('');
+  }
+
+  static allPrintable(maxThreshold) {
+    const values = [];
+    for(let i = 32; i <= 126; i++) {
+      values.push(String.fromCharCode(i));
+    }
+    return new Vocab(values, maxThreshold);
+  }
+
+  static fromString(string, maxThreshold) {
+    const values = String.prototype.concat(...new Set(string));
+    return new Vocab(values, maxThreshold);
+  }
+}
diff --git a/src/utilities/zeros.js b/src/utilities/zeros.js
index ee4b41a9d..b3e908bf1 100644
--- a/src/utilities/zeros.js
+++ b/src/utilities/zeros.js
@@ -1,4 +1,5 @@
 export default function zeros(size) {
+  if (typeof Float64Array !== 'undefined') return new Float64Array(size);
   let array = new Array(size);
   for (let i = 0; i < size; i++) {
     array[i] = 0;
diff --git a/test/unit/bitwise.js b/test/base/bitwise.js
similarity index 100%
rename from test/unit/bitwise.js
rename to test/base/bitwise.js
diff --git a/test/cross-validation/ocr.js b/test/base/cross-validation/ocr.js
similarity index 74%
rename from test/cross-validation/ocr.js
rename to test/base/cross-validation/ocr.js
index 1c652e51f..361c2eddf 100644
--- a/test/cross-validation/ocr.js
+++ b/test/base/cross-validation/ocr.js
@@ -1,7 +1,8 @@
 import canvas from 'canvas';
 import assert from 'assert';
-import brain from '../../src';
-import crossValidate from '../../src/cross-validate';
+import brain from '../../../src';
+import crossValidate from '../../../src/cross-validate';
+import shuffle from '../../utilities/shuffle';
 let dim = 24;
 
 function getSampling(context, letter, font) {
@@ -36,16 +37,35 @@ describe('OCR cross-validation', () => {
   it('recognize characters in different fonts', () => {
     let _canvas = new canvas(dim, dim);
     let context = _canvas.getContext('2d');
-    let letters = [
+    let characters = [
       'A',
       'B',
       'C',
       'D',
       'E',
+      'F',
+      'G',
+      'H',
+      'I',
+      'J',
       'K',
+      'L',
+      'M',
+      'N',
       'O',
+      'P',
+      'Q',
+      'R',
+      'S',
+      'T',
+      'U',
+      'V',
+      'W',
+      'X',
+      'Y',
       'Z'
     ];
+    //let randomCharacters = shuffle(characters).slice(0, 4);
     let fonts = [
       'Arial',
       'Courier',
@@ -66,7 +86,7 @@ describe('OCR cross-validation', () => {
     ];
     let data = [];
 
-    letters.forEach((letter) => {
+    characters.forEach((letter) => {
        fonts.forEach((font) => {
           let input = getSampling(context, letter, font);
 
@@ -81,12 +101,14 @@ describe('OCR cross-validation', () => {
     let trainOpts = { log: console.log, errorThresh: 0.08 };
     let result = crossValidate(brain.NeuralNetwork, data, opts, trainOpts);
 
-    console.log('\nMisclassifications:');
-    result.misclasses.forEach((misclass) => {
-      console.log('input: ' + misclass.input
-        + ' actual: ' + letters[misclass.actual]
-        + ' expected: ' + letters[misclass.expected] + '\n')
-    });
+    if (result.misclasses.length > 0) {
+      console.log('\nMisclassifications:');
+      result.misclasses.forEach((misslass) => {
+        console.log('input: ' + misclass.input
+          + ' actual: ' + characters[misclass.actual]
+          + ' expected: ' + characters[misclass.expected] + '\n')
+      });
+    }
 
     console.log('\nCross-validation of OCR data:\n');
     console.log(result.avgs);
diff --git a/test/unit/hash.js b/test/base/hash.js
similarity index 100%
rename from test/unit/hash.js
rename to test/base/hash.js
diff --git a/test/unit/json.js b/test/base/json.js
similarity index 100%
rename from test/unit/json.js
rename to test/base/json.js
diff --git a/test/unit/likely.js b/test/base/likely.js
similarity index 100%
rename from test/unit/likely.js
rename to test/base/likely.js
diff --git a/test/unit/lookup.js b/test/base/lookup.js
similarity index 100%
rename from test/unit/lookup.js
rename to test/base/lookup.js
diff --git a/test/unit/options.js b/test/base/options.js
similarity index 100%
rename from test/unit/options.js
rename to test/base/options.js
diff --git a/test/unit/stream-bitwise.js b/test/base/stream-bitwise.js
similarity index 92%
rename from test/unit/stream-bitwise.js
rename to test/base/stream-bitwise.js
index 2ea20abb5..6a0b84b09 100644
--- a/test/unit/stream-bitwise.js
+++ b/test/base/stream-bitwise.js
@@ -45,7 +45,7 @@ StreamTester.prototype = {
       let output = self.net.run(self.testData[i].input)[0];
       let target = self.testData[i].output;
       assert.ok(output < (target + self.wiggle) && output > (target - self.wiggle),
-        "failed to train " + self.op + " - output: " + output + " target: " + target);
+        'failed to train ' + self.op + ' - output: ' + output + ' target: ' + target);
     }
   }
 };
@@ -70,7 +70,7 @@ describe('bitwise functions', () => {
       input: [1],
       output: [0]
     }];
-    testBitwise(not, "not");
+    testBitwise(not, 'not');
   });
 
   it('XOR function', () => {
@@ -87,7 +87,7 @@ describe('bitwise functions', () => {
       input: [1, 1],
       output: [0]
     }];
-    testBitwise(xor, "xor");
+    testBitwise(xor, 'xor');
   });
 
   it('OR function', () => {
@@ -104,7 +104,7 @@ describe('bitwise functions', () => {
       input: [1, 1],
       output: [1]
     }];
-    testBitwise(or, "or");
+    testBitwise(or, 'or');
   });
 
   it('AND function', () => {
@@ -121,6 +121,6 @@ describe('bitwise functions', () => {
       input: [1, 1],
       output: [1]
     }];
-    testBitwise(and, "and");
+    testBitwise(and, 'and');
   });
 });
diff --git a/test/unit/trainopts.js b/test/base/trainopts.js
similarity index 100%
rename from test/unit/trainopts.js
rename to test/base/trainopts.js
diff --git a/test/recurrent/equation.js b/test/recurrent/equation.js
new file mode 100644
index 000000000..d66fcc892
--- /dev/null
+++ b/test/recurrent/equation.js
@@ -0,0 +1,214 @@
+import fs from 'fs';
+import assert from 'assert';
+import sinon from 'sinon';
+import Matrix from '../../src/recurrent/matrix';
+import OnesMatrix from '../../src/recurrent/matrix/ones-matrix';
+import Equation from '../../src/recurrent/matrix/equation';
+
+function randomMath() {
+  var left = Math.floor(Math.random() * 10);
+  var right = Math.floor(Math.random() * 10);
+  return left + '+' + right + '=' + (left + right);
+}
+
+function fourSquareMatrix(value) {
+  var result = new Matrix(4, 4);
+  result.weights.forEach((_, i) => {
+    result.weights[i] = value;
+  });
+  return result;
+}
+
+describe('equation', () => {
+  describe('run', () => {
+    it('calls all forwardFn properties', () => {
+      var equation = new Equation();
+      for (var i = 0; i < 10; i++) {
+        equation.states.push({
+          forwardFn: sinon.spy()
+        })
+      }
+      equation.run();
+      equation.states.forEach((state) => {
+        assert(state.forwardFn.called);
+      });
+    });
+  });
+  describe('runBack', () => {
+    it('calls all forwardFn properties', () => {
+      var equation = new Equation();
+      for (var i = 0; i < 10; i++) {
+        equation.states.push({
+          backpropagationFn: sinon.spy()
+        })
+      }
+      equation.runBackpropagate();
+      equation.states.forEach((state) => {
+        assert(state.backpropagationFn.called);
+      });
+    });
+  });
+  describe('add', () => {
+    it('calls forwardFn', () => {
+      var equation = new Equation();
+      var input = fourSquareMatrix(1);
+      equation.add(input, fourSquareMatrix(1));
+      assert.equal(equation.states.length, 1);
+      sinon.spy(equation.states[0], 'forwardFn');
+      equation.run();
+      assert(equation.states[0].forwardFn.called);
+    });
+  });
+  describe('multiply', () => {
+    it('calls forwardFn', () => {
+      var equation = new Equation();
+      var input = fourSquareMatrix(1);
+      equation.multiply(input, fourSquareMatrix(1));
+      assert.equal(equation.states.length, 1);
+      sinon.spy(equation.states[0], 'forwardFn');
+      equation.run();
+      assert(equation.states[0].forwardFn.called);
+    });
+  });
+  describe('multiplyElement', () => {
+    it('calls forwardFn', () => {
+      var equation = new Equation();
+      var input = fourSquareMatrix(1);
+      equation.add(input, fourSquareMatrix(1));
+      assert.equal(equation.states.length, 1);
+      sinon.spy(equation.states[0], 'forwardFn');
+      equation.run();
+      assert(equation.states[0].forwardFn.called);
+    });
+  });
+  describe('relu', () => {
+    it('calls forwardFn', () => {
+      var equation = new Equation();
+      var input = fourSquareMatrix(1);
+      equation.add(input, fourSquareMatrix(1));
+      assert.equal(equation.states.length, 1);
+      sinon.spy(equation.states[0], 'forwardFn');
+      equation.run();
+      assert(equation.states[0].forwardFn.called);
+    });
+  });
+  describe('inputMatrixToRow', () => {
+    it('calls forwardFn', () => {
+      var equation = new Equation();
+      var input = fourSquareMatrix(1);
+      equation.add(input, fourSquareMatrix(1));
+      assert.equal(equation.states.length, 1);
+      sinon.spy(equation.states[0], 'forwardFn');
+      equation.run();
+      assert(equation.states[0].forwardFn.called);
+    });
+  });
+  describe('sigmoid', () => {
+    it('calls forwardFn', () => {
+      var equation = new Equation();
+      var input = fourSquareMatrix(1);
+      equation.add(input, fourSquareMatrix(1));
+      assert.equal(equation.states.length, 1);
+      sinon.spy(equation.states[0], 'forwardFn');
+      equation.run();
+      assert(equation.states[0].forwardFn.called);
+    });
+  });
+  describe('tanh', () => {
+    it('calls forwardFn', () => {
+      var equation = new Equation();
+      var input = fourSquareMatrix(1);
+      equation.add(input, fourSquareMatrix(1));
+      assert.equal(equation.states.length, 1);
+      sinon.spy(equation.states[0], 'forwardFn');
+      equation.run();
+      assert(equation.states[0].forwardFn.called);
+    });
+  });
+  describe('nesting', () => {
+    it('can nest 3 deep and run forward', () => {
+      var equation = new Equation();
+      var input = fourSquareMatrix(2);
+      equation.multiply(equation.multiply(equation.multiply(input, fourSquareMatrix(2)), fourSquareMatrix(2)), fourSquareMatrix(2));
+      assert.equal(equation.states.length, 3);
+      sinon.spy(equation.states[0], 'forwardFn');
+      sinon.spy(equation.states[1], 'forwardFn');
+      sinon.spy(equation.states[2], 'forwardFn');
+      equation.run();
+      equation.states.forEach((state) => {
+        assert(state.forwardFn.called);
+      });
+    });
+    it('can nest 3 deep and run backward', () => {
+      var equation = new Equation();
+      var input = fourSquareMatrix(2);
+      equation.tanh(equation.multiply(equation.add(input, fourSquareMatrix(2)), fourSquareMatrix(2)), fourSquareMatrix(2));
+      assert.equal(equation.states.length, 3);
+      sinon.spy(equation.states[0], 'backpropagationFn');
+      sinon.spy(equation.states[1], 'backpropagationFn');
+      sinon.spy(equation.states[2], 'backpropagationFn');
+      equation.runBackpropagate();
+      equation.states.forEach((state) => {
+        assert(state.backpropagationFn.called);
+      });
+    });
+  });
+  describe('inputMatrixToRow', () => {
+    context('run', () => {
+      it('can properly split up a matrix', () => {
+        var input = new Matrix(2, 2);
+        /**
+         * Matrix like:
+         * 1 1
+         * 2 2
+         */
+        input.weights.forEach((w, i) => {
+          if (i < 2) {
+            input.weights[i] = 1;
+          } else {
+            input.weights[i] = 2;
+          }
+        });
+        var equation = new Equation();
+        equation.add(new OnesMatrix(1, 2), equation.inputMatrixToRow(input));
+        var output = equation.run();
+        assert.equal(output.weights.length, 2);
+        assert.equal(output.weights[0], 2);
+        assert.equal(output.weights[1], 2);
+
+        output = equation.run(1);
+        assert.equal(output.weights.length, 2);
+        assert.equal(output.weights[0], 3);
+        assert.equal(output.weights[1], 3);
+      });
+    });
+    context('runBackpropagate', () => {
+      it('can properly split up a matrix', () => {
+        var input = new Matrix(2, 2);
+        /**
+         * Matrix like:
+         * 1 1
+         * 2 2
+         */
+        input.weights.forEach((w, i) => {
+          if (i < 2) {
+            input.weights[i] = 1;
+          } else {
+            input.weights[i] = 2;
+          }
+        });
+        var equation = new Equation();
+        equation.add(new OnesMatrix(1, 2), equation.inputMatrixToRow(input));
+        var output = equation.run();
+        assert.equal(output.weights.length, 2);
+        output = equation.run(1);
+        assert.equal(output.weights.length, 2);
+        output.weights.forEach((weight, i) => {
+          output.recurrence[i] = weight;
+        });
+        equation.runBackpropagate(1);
+        equation.runBackpropagate();
+      });
+    });
+  });
+});
\ No newline at end of file
diff --git a/test/recurrent/gru.js b/test/recurrent/gru.js
new file mode 100644
index 000000000..9cf5826c0
--- /dev/null
+++ b/test/recurrent/gru.js
@@ -0,0 +1,126 @@
+import assert from 'assert';
+import GRU from '../../src/recurrent/gru';
+import Vocab from '../../src/utilities/vocab';
+
+function randomMath() {
+  var left = Math.floor(Math.random() * 10);
+  var right = Math.floor(Math.random() * 10);
+  return left + '+' + right + '=' + (left + right);
+}
+
+describe('gru', () => {
+  describe('math', () => {
+    it('can predict what a math problem is after being fed 1000 random math problems', () => {
+      const vocab = new Vocab(['0','1','2','3','4','5','6','7','8','9','+','=', '-', '/', '*']);
+      console.time('math gru');
+      var net = new GRU({
+        inputSize: vocab.characters.length,
+        inputRange: vocab.characters.length,
+        outputSize: vocab.characters.length
+      });
+
+      for (var i = 0; i < 1000; i++) {
+        net.run(vocab.toIndexes(randomMath()));
+        if (i % 10 === 0) {
+          console.log(vocab.toCharacters(net.predict()).join(''));
+        }
+      }
+
+      var prediction = vocab.toCharacters(net.predict()).join('');
+      assert(/[+]/.test(prediction));
+      assert(/[=]/.test(prediction));
+      console.timeEnd('math gru');
+      console.log(prediction);
+    });
+  });
+
+  describe('printable characters', () => {
+    it('can learn a phrase', (done) => {
+      const phrase = 'hello world;|something I comment about';
+      const vocab = Vocab.fromString(phrase);
+      var net = new GRU({
+        inputSize: 40,
+        inputRange: vocab.characters.length,
+        outputSize: 40
+      });
+
+      console.time('math lstm');
+      for (var i = 0; i < 500; i++) {
+        net.run(vocab.toIndexes(phrase));
+        if (i % 10 === 0) {
+          console.log(vocab.toCharacters(net.predict()).join(''));
+        }
+      }
+      console.timeEnd('math lstm');
+      console.log('');
+      assert.equal(vocab.toCharacters(net.predict()).join(''), phrase);
+      done();
+    });
+
+    it('can predict a phrase when given the first letter', (done) => {
+      const phrase = 'bob';
+      const vocab = new Vocab(['b', 'o']);
+      var net = new GRU({
+        inputSize: 3,
+        inputRange: vocab.characters.length,
+        outputSize: 3
+      });
+
+      console.time('math lstm');
+      for (var i = 0; i < 100; i++) {
+        net.run(vocab.toIndexes(phrase));
+        if (i % 10 === 0) {
+          console.log(vocab.toCharacters(net.predict()).join(''));
+        }
+      }
+      console.timeEnd('math lstm');
+      console.log('');
+      assert.equal(vocab.toCharacters(net.predict(vocab.toIndexes('b'))).join(''), phrase);
+      done();
+    });
+
+    it('can learn a phrase, export it to a function, and it still runs', (done) => {
+      const phrase = 'hello world;|something I comment about';
+      const vocab = Vocab.fromString(phrase);
+      const phraseAsIndices = vocab.toIndexes(phrase);
+      var net = new GRU({
+        inputSize: 40,
+        inputRange: vocab.characters.length,
+        outputSize: 40
+      });
+
+      console.time('math lstm');
+      for (var i = 0; i < 1000; i++) {
+        net.run(phraseAsIndices);
+        if (i % 10 === 0) {
+          console.log(vocab.toCharacters(net.predict()).join(''));
+        }
+      }
+      console.timeEnd('math lstm');
+      console.log('');
+      assert.equal(vocab.toCharacters(net.predict()).join(''), phrase);
+      done();
+    });
+  });
+
+  describe('.toFunction', () => {
+    it('can output same as run method', () => {
+      const vocab = new Vocab(['h', 'i', ' ', 'm', 'o', '!']);
+      var net = new GRU({
+        inputSize: 7,
+        inputRange: vocab.characters.length,
+        outputSize: 7
+      });
+
+      for (var i = 0; i < 100; i++) {
+        net.run(vocab.toIndexes('hi mom!'));
+        if (i % 10) {
+          console.log(vocab.toCharacters(net.predict()).join(''));
+        }
+      }
+
+      var lastOutput = vocab.toCharacters(net.predict()).join('');
+      assert.equal(vocab.toCharacters(net.toFunction()()).join(''), lastOutput);
+    });
+  });
+});
diff --git a/test/recurrent/lstm.js b/test/recurrent/lstm.js
new file mode 100644
index 000000000..99069624d
--- /dev/null
+++ b/test/recurrent/lstm.js
@@ -0,0 +1,49 @@
+import assert from 'assert';
+import LSTM from '../../src/recurrent/lstm';
+import Vocab from '../../src/utilities/vocab';
+import { vocab, build, train } from '../utilities/math-addition-vocab';
+
+function runAgainstMath(rnn) {
+  train(rnn);
+  var prediction = vocab.toCharacters(rnn.predict()).join('');
+  console.log(prediction);
+  assert(/^[0-9]+[+][0-9]+[=][0-9]+$/.test(prediction));
+}
+
+describe('lstm', () => {
+  describe('math', () => {
+    it('can predict what a math problem is after being fed 1000 random math problems', () => {
+      var net = new LSTM({
+        inputSize: 6,
+        inputRange: vocab.characters.length,
+        outputSize: vocab.characters.length
+      });
+
+      console.time('math lstm');
+      runAgainstMath(net);
+      console.timeEnd('math lstm');
+      console.log('');
+    });
+  });
+
+  describe('.toFunction', () => {
+    it('can output same as run method', () => {
+      const vocab = new Vocab(['h', 'i', ' ', 'm', 'o', '!']);
+      var net = new LSTM({
+        inputSize: 7,
+        inputRange: vocab.characters.length,
+        outputSize: 7
+      });
+
+      for (var i = 0; i < 100; i++) {
+        net.run(vocab.toIndexes('hi mom!'));
+        if (i % 10) {
+          console.log(vocab.toCharacters(net.predict()).join(''));
+        }
+      }
+
+      var lastOutput = vocab.toCharacters(net.predict()).join('');
+      assert.equal(vocab.toCharacters(net.toFunction()()).join(''), lastOutput);
+    });
+  });
+});
diff --git a/test/recurrent/matrix/add.js b/test/recurrent/matrix/add.js
new file mode 100644
index 000000000..927d6a82b
--- /dev/null
+++ b/test/recurrent/matrix/add.js
@@ -0,0 +1,52 @@
+import assert from 'assert';
+import Matrix from '../../../src/recurrent/matrix';
+import add from '../../../src/recurrent/matrix/add';
+import addB from '../../../src/recurrent/matrix/add-b';
+
+describe('matrix', () => {
+  describe('add', () => {
+    context('when given a left and right matrix both of 2 rows and 2 columns', () => {
+      it('', () => {
+        var m1 = Matrix.fromArray([
+          [0, 2],
+          [4, 6]
+        ]);
+        var m2 = Matrix.fromArray([
+          [0, 2],
+          [4, 6]
+        ]);
+        var result = new Matrix(2, 2);
+        add(result, m1, m2);
+        var weights = [0, 4, 8, 12];
+        assert.equal(result.weights.length, 4);
+        result.weights.forEach((value, i) => {
+          assert.equal(value, weights[i]);
+        });
+      });
+    });
+  });
+
+  describe('addB', () => {
+    context('when given a left and right matrix both of 2 rows and 2 columns', () => {
+      it('', () => {
+        var m1 = new Matrix(2, 2);
+        var m2 = new Matrix(2, 2);
+        var result = Matrix.fromArray([
+          [0, 2],
+          [4, 6]
+        ]);
+        addB(result, m1, m2);
+        var recurrence = [0, 2, 4, 6];
+
+        assert.equal(m1.recurrence.length, 4);
+        m1.recurrence.forEach((value, i) => {
+          assert.equal(value, recurrence[i]);
+        });
+        assert.equal(m2.recurrence.length, 4);
+        m2.recurrence.forEach((value, i) => {
+          assert.equal(value, recurrence[i]);
+        });
+      });
+    });
+  });
+});
\ No newline at end of file
diff --git a/test/recurrent/matrix/index.js b/test/recurrent/matrix/index.js
new file mode 100644
index 000000000..af2785c50
--- /dev/null
+++ b/test/recurrent/matrix/index.js
@@ -0,0 +1,28 @@
+import assert from 'assert';
+import Matrix from '../../../src/recurrent/matrix';
+
+describe('matrix', () => {
+  it('.fromArray', () => {
+    var m1 = Matrix.fromArray([
+      [2, 2],
+      [2, 2]
+    ]);
+
+    assert.equal(m1.weights.length, 4);
+    assert.equal(m1.recurrence.length, 4);
+    m1.weights.forEach(function(value, i) {
+      assert.equal(value, 2);
+      assert.equal(m1.recurrence[i], 2);
+    });
+  });
+
+  describe('instantiation', () => {
+    context('when given 5 rows and 5 columns', () => {
+      it('will have a weight and recurrence length of 25', () => {
+        var m = new Matrix(5, 5);
+        assert.equal(m.weights.length, 25);
+        assert.equal(m.recurrence.length, 25);
+      });
+    });
+  });
+});
\ No newline at end of file
diff --git a/test/recurrent/matrix/multiply-element.js b/test/recurrent/matrix/multiply-element.js
new file mode 100644
index 000000000..012c45036
--- /dev/null
+++ b/test/recurrent/matrix/multiply-element.js
@@ -0,0 +1,60 @@
+import assert from 'assert';
+import Matrix from '../../../src/recurrent/matrix';
+import multiplyElement from '../../../src/recurrent/matrix/multiply-element';
+import multiplyElementB from '../../../src/recurrent/matrix/multiply-element-b';
+
+describe('matrix', () => {
+  describe('multiplyElement', () => {
+    context('when given a left and right matrix both of 2 rows and 2 columns', () => {
+      it('correctly multiplies the values', () => {
+        var m1 = Matrix.fromArray([
+          [2, 2],
+          [2, 2]
+        ]);
+        var m2 = Matrix.fromArray([
+          [2, 2],
+          [2, 2]
+        ]);
+        var result = Matrix.fromArray([
+          [4, 4],
+          [4, 4]
+        ]);
+        multiplyElement(result, m1, m2);
+        assert.equal(result.weights.length, 4);
+        result.weights.forEach((value, i) => {
+          assert.equal(value, 4);
+        });
+      });
+    });
+  });
+
+  describe('multiplyElementB', () => {
+    //not even yet used
+    context('when given a left and right matrix both of 2 rows and 2 columns', () => {
+      it('correctly multiplies the values', () => {
+        var m1 = Matrix.fromArray([
+          [2, 2],
+          [2, 2]
+        ]);
+        var m2 = Matrix.fromArray([
+          [2, 2],
+          [2, 2]
+        ]);
+        var result = Matrix.fromArray([
+          [4, 4],
+          [4, 4]
+        ]);
+        multiplyElementB(result, m1, m2);
+        assert.equal(m1.recurrence.length, 4);
+        m1.recurrence.forEach((value, i) => {
+          assert.equal(value, 10);
+        });
+
+        assert.equal(m2.recurrence.length, 4);
+        m2.recurrence.forEach((value, i) => {
+          assert.equal(value, 10);
+        });
+      });
+    });
+  });
+});
\ No newline at end of file
diff --git a/test/recurrent/matrix/multiply.js b/test/recurrent/matrix/multiply.js
new file mode 100644
index 000000000..afdaa2b53
--- /dev/null
+++ b/test/recurrent/matrix/multiply.js
@@ -0,0 +1,54 @@
+import assert from 'assert';
+import Matrix from '../../../src/recurrent/matrix';
+import multiply from '../../../src/recurrent/matrix/multiply';
+import multiplyB from '../../../src/recurrent/matrix/multiply-b';
+
+describe('matrix', () => {
+  describe('multiply', () => {
+    context('when given a left and right matrix both of 2 rows and 2 columns', () => {
+      it('correctly multiplies the values', () => {
+        var m1 = Matrix.fromArray([
+          [2, 2],
+          [2, 2]
+        ]);
+        var m2 = Matrix.fromArray([
+          [2, 2],
+          [2, 2]
+        ]);
+        var result = new Matrix(2, 2);
+        multiply(result, m1, m2);
+        var weights = [8, 8, 8, 8];
+        assert.equal(result.weights.length, 4);
+        result.weights.forEach((value, i) => {
+          assert.equal(value, weights[i]);
+        });
+      });
+    });
+  });
+
+  describe('multiplyB', () => {
+    context('when given a left and right matrix both of 2 rows and 2 columns', () => {
+      it('correctly multiplies the values', () => {
+        var m1 = Matrix.fromArray([
+          [3, 3],
+          [3, 3]
+        ]);
+        var m2 = Matrix.fromArray([
+          [3, 3],
+          [3, 3]
+        ]);
+        var result = Matrix.fromArray([
+          [3, 3],
+          [3, 3]
+        ]);
+        multiplyB(result, m1, m2);
+        m1.recurrence.forEach((value) => {
+          assert.equal(value, 21);
+        });
+        m2.recurrence.forEach((value) => {
+          assert.equal(value, 21);
+        });
+      });
+    });
+  });
+});
\ No newline at end of file
diff --git a/test/recurrent/matrix/softmax.js b/test/recurrent/matrix/softmax.js
new file mode 100644
index 000000000..ce6e4522d
--- /dev/null
+++ b/test/recurrent/matrix/softmax.js
@@ -0,0 +1,19 @@
+import assert from 'assert';
+import Matrix from '../../../src/recurrent/matrix';
+import softmax from '../../../src/recurrent/matrix/softmax';
+
+describe('matrix', () => {
+  describe('softmax', () => {
+    context('when given a left and right matrix both of 2 rows and 2 columns', () => {
+      it('correctly multiplies the values', () => {
+        var m1 = softmax(Matrix.fromArray([
+          [2, 2],
+          [2, 2]
+        ]));
+        m1.weights.forEach((value) => {
+          assert.equal(value, 0.25);
+        });
+      });
+    });
+  });
+});
\ No newline at end of file
diff --git a/test/recurrent/phrase-writer.js b/test/recurrent/phrase-writer.js
new file mode 100644
index 000000000..5e0a1a7a9
--- /dev/null
+++ b/test/recurrent/phrase-writer.js
@@ -0,0 +1,105 @@
+import fs from 'fs';
+import RNN from '../../src/recurrent/rnn';
+import LSTM from '../../src/recurrent/lstm';
+import phraseWriterJson from './phrase-writer.json';
+var vocabData = initVocab();
+
+function initVocab(maxThreshold) {
+  maxThreshold = maxThreshold || 0;
+  var phrases = phraseWriterJson;
+  // go over all characters and keep track of all unique ones seen
+  var txt = phrases.join(''); // concat all
+
+  // count up all characters
+  var d = {};
+  for(var i = 0, n = txt.length; i < n; i++) {
+    var txti = txt[i];
+    if(txti in d) {
+      d[txti] += 1;
+    } else {
+      d[txti] = 1;
+    }
+  }
+
+  // filter by count threshold and create pointers
+  var characterToIndex = {};
+  var indexToCharacter = {};
+  var vocab = [];
+  // NOTE: start at one because we will have START and END tokens!
+  // that is, START token will be index 0 in model letter vectors
+  // and END token will be index 0 in the next character softmax
+  var q = 1;
+  for(var ch in d) {
+    if(d.hasOwnProperty(ch)) {
+      if(d[ch] >= maxThreshold) {
+        // add character to vocab
+        characterToIndex[ch] = q;
+        indexToCharacter[q] = ch;
+        vocab.push(ch);
+        q++;
+      }
+    }
+  }
+
+  return {
+    phrases: phrases,
+    characterToIndex: characterToIndex,
+    indexToCharacter: indexToCharacter,
+    distinct: vocab.join(''),
+    inputSize: vocab.length + 1,
+    outputSize: vocab.length + 1,
+    epochSize: phrases.length
+  };
+}
+
+function phraseToIndexes(phrase, maxThreshold) {
+  maxThreshold = maxThreshold || 0;
+  var result = [];
+  var characterToIndex = vocabData.characterToIndex;
+
+  for (var i = 0, max = phrase.length; i < max; i++) {
+    var character = phrase[i];
+    var index = characterToIndex[character];
+    if (index < maxThreshold) continue;
+    result.push(index);
+  }
+
+  return result;
+}
+
+function indexesToPhrase(indexes, maxThreshold) {
+  maxThreshold = maxThreshold || 0;
+  var result = [];
+  var indexToCharacter = vocabData.indexToCharacter;
+
+  for (var i = 0, max = indexes.length; i < max; i++) {
+    var index = indexes[i];
+    if (index < maxThreshold) continue;
+    var character = indexToCharacter[index];
+    result.push(character);
+  }
+
+  return result;
+}
+
+function randomPhrase() {
+  return vocabData.phrases[Math.floor(Math.random() * vocabData.phrases.length)];
+}
+
+describe('character', () => {
+  it('', () => {
+    return;
+    var rnn = new LSTM({
+      inputSize: vocabData.inputSize,
+      outputSize: vocabData.outputSize
+    });
+
+    for (var i = 0; i < 1000; i++) {
+      rnn.input(phraseToIndexes(randomPhrase()));
+    }
+
+    var prediction = rnn.predict();
+
+    console.log(indexesToPhrase(prediction).join(''));
+  });
+});
\ No newline at end of file
diff --git a/test/recurrent/phrase-writer.json b/test/recurrent/phrase-writer.json
new file mode 100644
index 000000000..578f177d4
--- /dev/null
+++ b/test/recurrent/phrase-writer.json
@@ -0,0 +1,1431 @@
+["the company has, say, 6 months of runway",
+"or to put it more brutally, 6 months before they're out of business",
+"they expect to avoid that by raising more from investors",
+"that last sentence is the fatal one",
+"it's hard to convince investors the first time too, but founders expect that",
+"what bites them the second time is a confluence of three forces:",
+"the company is spending more now than it did the first time it raised money",
+"investors have much higher standards for companies that have already raised money",
+"the company is now starting to read as a failure",
+"the first time it raised money, it was neither a success nor a failure; it was too early to ask",
+"i'm going to call the situation i described in the first paragraph \"the fatal pinch",
+"one of the things that makes the fatal pinch so dangerous is that it's self-reinforcing",
+"y combinator tells founders who raise money to act as if it's the last they'll ever get",
+"i will now, by an amazing feat of clairvoyance, do this for you: the probability is zero",
+"you should shut down the company if you're certain it will fail no matter what you do",
+"companies rarely have to fail though",
+"what i'm really doing here is giving you the option of admitting you've already given up",
+"if you don't want to shut down the company, that leaves increasing revenues and decreasing expenses",
+"in most startups, expenses  people and decreasing expenses  firing people",
+"if so, now's the time",
+"which leaves two options, firing good people and making more money",
+"you should lean more toward firing people if the source of your trouble is overhiring",
+"plus those 15 people might not even be the ones you need for whatever you end up building",
+"so the solution may be to shrink and then figure out what direction to grow in",
+"it may seem facile to suggest a startup make more money, as if that could be done for the asking",
+"usually a startup is already trying as hard as it can to sell whatever it sells",
+"but only work on whatever will get you the most revenue the soonest",
+"or you may have expertise in some new field they don't understand",
+"and to the extent you can, try to avoid the worst pitfalls of consulting",
+"you keep the ip and no billing by the hour",
+"you just have to realize in time that you're near death",
+"and if you're in the fatal pinch, you are",
+"it struck me recently how few of the most successful people i know are mean",
+"there are exceptions, but remarkably few",
+"meanness isn't rare",
+"in fact, one of the things the internet has shown us is how mean people can be",
+"a few decades ago, only famous people and professional writers got to publish their opinions",
+"now everyone can, and we can all see the long tail of meanness that had previously been hidden",
+"what's going on here? are meanness and success inversely correlated?",
+"part of what's going on, of course, is selection bias",
+"i only know people who work in certain fields: startup founders, programmers, professors",
+"i'm willing to believe that successful people in other fields are mean",
+"maybe successful hedge fund managers are mean; i don't know enough to say",
+"it seems quite likely that most successful drug lords are mean",
+"being married to her is like standing next to an airport baggage scanner",
+"why? i think there are several reasons",
+"one is that being mean makes you stupid",
+"that's why i hate fights",
+"you never do your best work in a fight, because fights are not sufficiently general",
+"winning is always a function of the situation and the people involved",
+"and yet fighting is just as much work as thinking about real problems",
+"startups don't win by attacking",
+"they win by transcending",
+"there are exceptions of course, but usually the way to win is to race ahead, not to stop and fight",
+"another reason mean founders lose is that they can't get the best people to work for them",
+"they can hire people who will put up with them because they need a job",
+"but the best people have other options",
+"a mean person can't convince the best people to work for him unless he is super convincing",
+"and while having the best people helps any organization, it's critical for startups",
+"the startup founders who end up richest are not the ones driven by money",
+"[1] the ones who keep going are driven by something else",
+"they may not say so explicitly, but they're usually trying to improve the world",
+"which means people with a desire to improve the world have a natural advantage",
+"this kind of work is the future",
+"for most of history success meant control of scarce resources",
+"for most of history, success meant success at zero-sum games",
+"and in most of them meanness was not a handicap but probably an advantage",
+"that is changing",
+"increasingly the games that matter are not zero-sum",
+"there have long been games where you won by having new ideas",
+"in the third century bc archimedes won by doing that",
+"at least until an invading roman army killed him",
+"and not just not being at war",
+"people need to feel that what they create can't be stolen",
+"that has always been the case for thinkers, which is why this trend began with them",
+"the exciting thing is that their m",
+"seems to be spreading",
+"so i'm really glad i stopped to think about this",
+"jessica and i have always worked hard to teach our kids not to be mean",
+"we tolerate noise and mess and junk food, but not meanness",
+"startups are very counterintuitive",
+"i'm not sure why",
+"maybe it's just because knowledge about them hasn't permeated our culture yet",
+"but whatever the reason, starting a startup is a task where you can't always trust your instincts",
+"it's like skiing in that way",
+"when you first try skiing and you want to slow down, your instinct is to lean back",
+"but if you lean back on skis you fly down the hill out of control",
+"so part of learning to ski is learning to suppress that impulse",
+"eventually you get new habits, but at first it takes a conscious effort",
+"at first there's a list of things you're trying to remember as you start down the hill",
+"startups are as unnatural as skiing, so there's a similar list for startups",
+"counterintuitive",
+"if you know nothing more than this, you may at least pause before making them",
+"it's really true",
+"they seem wrong",
+"so of course your first impulse is to disregard them",
+"if founders' instincts already gave them the right answers, they wouldn't need us",
+"you only need other people to give you advice that surprises you",
+"that's why there are a lot of ski instructors and not many running instructors",
+"you can, however, trust your instincts about people",
+"and in fact one of the most common mistakes young founders make is not to do that enough",
+"if someone seems slippery, or bogus, or a jerk, don't ignore it",
+"this is one case where it pays to be self-indulgent",
+"work with people you genuinely like, and you've known long enough to be sure",
+"the second counterintuitive point is that it's not that important to know a lot about startups",
+"mark zuckerberg didn't succeed because he was an expert on startups",
+"if you don't know anything about, say, how to raise an angel round, don't feel bad on that account",
+"that sort of thing you can learn when you need to, and forget after you've done it",
+"\" it would set off alarms",
+"from the outside that seems like what startups do",
+"we saw this happen so often that we made up a name for it: playing house",
+"eventually i realized why it was happening",
+"think about what you have to do to get into college, for example",
+"extracurricular activities, check",
+"even in college classes most of the work is as artificial as running laps",
+"i'm not attacking the educational system for being this way",
+"i confess i did it myself in college",
+"it was like a game",
+"then they want to know what the tricks are for growing fast",
+"and we have to tell them the best way to do that is simply to make something people want",
+"\" and the partner replying \"just",
+"gaming the system may continue to work if you go to work for a big company",
+"[2] but that doesn't work with startups",
+"startups are as impersonal as physics",
+"you have to make something people want, and you prosper only to the extent you do",
+"the dangerous thing is, faking does work to some degree on investors",
+"but it's not in your interest to",
+"the company is ultimately doomed",
+"all you're doing is wasting your own time riding it down",
+"so stop looking for the trick",
+"it's exciting that there even exist parts of the world where you win by doing good work",
+"how do you win in each type of work, and what would you like to win by doing? [4]",
+"all-consuming",
+"that brings us to our fourth counterintuitive point: startups are all-consuming",
+"if you start a startup, it will take over your life to a degree you cannot imagine",
+"so there is a real opportunity cost here",
+"larry page may seem to have an enviable life, but there are aspects of it that are unenviable",
+"if he goes on vacation for even a week, a whole week's backlog of shit accumulates",
+"it never gets any easier",
+"the nature of the problems change",
+"but the total volume of worry never decreases; if anything it increases",
+"many of which will make you a better parent when you do have kids",
+"and since you can delay pushing the button for a while, most people in rich countries do",
+"to be fair, the universities have their hand forced here",
+"a lot of incoming students are interested in startups",
+"universities are, at least de facto, expected to prepare them for their careers",
+"so students who want to start startups hope universities can teach them about startups",
+"can universities teach students about startups? yes and no",
+"[5] so starting a startup is intrinsically something you can only really learn by doing it",
+"you may be nominally a student for a bit, but you won't even be that for long",
+"do not start a startup in college",
+"starting a startup is like a brutally fast depth-first search",
+"most people should still be searching breadth-first at 20",
+"if you start a startup at 20 and you're sufficiently successful, you'll never get to do it",
+"mark zuckerberg will never get to bum around a foreign country",
+"he can do other things most people can't, like charter jets to fly him to foreign countries",
+"but success has taken a lot of the serendipity out of his life",
+"facebook is running him as much as he's running facebook",
+"among other things it gives you more options to choose your life's work from",
+"there's not even a tradeoff here",
+"should you do it at any age? i realize i've made startups sound pretty hard",
+"if i haven't, let me try again: starting a startup is really hard",
+"what if it's too hard? how can you tell if you're up to this challenge?",
+"the answer is the fifth counterintuitive point: you can't tell",
+"starting a startup will change you a lot",
+"it was easy to tell how smart they were, and most people reading this will be over that threshold",
+"the hard part was predicting how tough and ambitious they would become",
+"the founders sometimes think they know",
+"if you're absolutely terrified of starting a startup, you probably shouldn't do it",
+"but if you're merely unsure whether you're up to it, the only way to find out is to try",
+"just not now",
+"for getting both is the same",
+"i've written a whole essay on this, so i won't repeat it all here",
+"the way to come up with good startup ideas is to take a step back",
+"in fact, so unconsciously that you don't even realize at first that they're startup ideas",
+"this is not only possible, it's how apple, yahoo, google, and facebook all got started",
+"none of these companies were even meant to be companies at first",
+"they were all just side projects",
+"the third part, incidentally, is how you get cofounders at the same time as the idea",
+"\" but that prescription, though sufficient, is too narrow",
+"what was special about brian chesky and joe gebbia was not that they were experts in technology",
+"what kind of problems are those? that is very hard to answer in the general case",
+"so how do you know when you're working on real stuff? [8]",
+"i know how i know",
+"y combinator itself was something i only did because it seemed interesting",
+"so i seem to have some sort of internal compass that helps me out",
+"but i don't know what other people have in their heads",
+"and indeed, probably also the best way to live",
+"you may not realize they're startup ideas, but you'll know they're something that ought to exist",
+"he didn't mean it to be a startup, and he never tried to turn it into one",
+"\" it's the classic version of college as education for its own sake",
+"the component of entrepreneurship that really matters is domain expertise",
+"the way to become larry page was to become an expert on search",
+"at its best, starting a startup is merely an ulterior motive for curiosity",
+"and you'll do it best if you introduce the ulterior motive toward the end of the process",
+"most startups that raise money do it more than once",
+"reality can be messier",
+"some companies raise money twice in phase 2",
+"others skip phase 1 and go straight to phase 2",
+"but the three phase path is at least the one about which individual startups' paths oscillate",
+"this essay focuses on phase 2 fundraising",
+"that problem is irreducible; it should be hard",
+"but much of the other kind of difficulty can be eliminated",
+"you can't trust your intuitions",
+"i'm going to give you a set of rules here that will get you through this process if anything will",
+"at certain moments you'll be tempted to ignore them",
+"so rule number zero is: these rules exist for a reason",
+"the ultimate source of the forces acting on you are the forces acting on investors",
+"but that fast growth means investors can't wait around",
+"if you wait till a startup is obviously a success, it's too late",
+"but that in turn makes investors nervous they're about to invest in a flop",
+"as indeed they often are",
+"what investors would like to do, if they could, is wait",
+"but if you wait too long, other investors might take the deal away from you",
+"and of course the other investors are all subject to the same forces",
+"don't raise money unless you want it and it wants you",
+"actually it isn't",
+"rapid growth is what makes a company a startup",
+"the other time not to raise money is when you won't be able to",
+"be in fundraising mode or not",
+"one of the things that surprises founders most about fundraising is how distracting it is",
+"when you start fundraising, everything else grinds to a halt",
+"the problem is not the time fundraising consumes but that it becomes the top idea in your mind",
+"a startup can't endure that level of distraction for long",
+"because fundraising is so distracting, a startup should either be in fundraising mode or not",
+"you can take money from investors when you're not in fundraising mode",
+"you just can't expend any attention on it",
+"there are two things that take attention: convincing investors, and negotiating with them",
+"[3] the terms will be whatever they turn out to be in your next equity round",
+"investors will try to lure you into fundraising when you're not",
+"it's great for them if they can, because they can thereby get a shot at you before everyone else",
+"they'll send you emails saying they want to meet to learn more about you",
+"deals don't happen that way",
+"they may say they just want to meet and chat, but investors never just want to meet and chat",
+"get introductions to investors",
+"before you can talk to investors, you have to be introduced to them",
+"if you're presenting at a demo day, you'll be introduced to a whole bunch simultaneously",
+"but even if you are, you should supplement these with intros you collect yourself",
+"do you have to be introduced? in phase 2, yes",
+"intros vary greatly in effectiveness",
+"the best type of intro is from a well-known investor who has just invested in you",
+"so when you get an investor to commit, ask them to introduce you to other investors they respect",
+"[7] the next best type of intro is from a founder of a company they've funded",
+"you can also get intros from other people in the startup community, like lawyers and reporters",
+"there are now sites like angellist, fundersclub, and wefunder that can introduce you to investors",
+"we recommend startups treat them as auxiliary sources of money",
+"raise money first from leads you get yourself",
+"those will on average be better investors",
+"hear no till you hear yes",
+"i mentioned earlier that investors prefer to wait if they can",
+"what's particularly dangerous for founders is the way they wait",
+"essentially, they lead you on",
+"they seem like they're about to invest right up till the moment they say no",
+"if they even say no",
+"some of the worse ones never actually do say no; they just stop replying to your emails",
+"they hope that way to get a free option on investing",
+"that's not the worst thing investors will do",
+"and wishful thinking founders are happy to meet them half way",
+"fortunately, the next rule is a tactic for neutralizing this behavior",
+"but to work it depends on you not being tricked by the no that sounds like yes",
+"if you believe an investor has committed, get them to confirm it",
+"and till they confirm, regard them as saying no",
+"do breadth-first search weighted by expected value",
+"when you talk to investors your m",
+"should be breadth-first search, weighted by expected value",
+"you should always talk to investors in parallel rather than serially",
+"meet such investors last, if at all",
+"but you have to be disciplined about assigning probabilities",
+"you can't let how much you want an investor influence your estimate of how much they want you",
+"know where you stand",
+"never leave a meeting with an investor without asking what happens next",
+"if you're experienced at negotiations, you already know how to ask such questions",
+"[13] if you're not, there's a trick you can use in this situation",
+"investors know you're inexperienced at raising money",
+"inexperience there doesn't make you unattractive",
+"larry and sergey were noobs at fundraising",
+"get the first commitment",
+"the biggest factor in most investors' opinions of you is the opinion of other investors",
+"once you start getting investors to commit, it becomes increasingly easy to get more to",
+"but the other side of this coin is that it's often hard to get the first commitment",
+"getting the first substantial offer can be half the total difficulty of fundraising",
+"what counts as a substantial offer depends on who it's from and how much it is",
+"money from friends and family doesn't usually count, no matter how much",
+"close committed money",
+"it's not a deal till the money's in the bank",
+"and it's also one that furnishes them plenty of excuses to gratify it",
+"the public markets snap startup investing around like a whip",
+"if the chinese economy blows up tomorrow, all bets are off",
+"tomorrow a big competitor could appear, or you could get cded, or your cofounder could quit",
+"even a day's delay can bring news that causes an investor to change their mind",
+"so when someone commits, get the money",
+"knowing where you stand doesn't end when they say they'll invest",
+"inexperienced investors are the ones most likely to get buyer's remorse",
+"but i've heard of cases of even top-tier vc firms welching on deals",
+"avoid investors who don't \"lead",
+"some investors are known for deciding quickly, and those are extra valuable early on",
+"conversely, an investor who will only invest once other investors have is worthless initially",
+"you can recognize this contemptible subspecies of investor because they often talk about \"leads",
+"\" they say that they don't lead, or that they'll invest once you have a lead",
+"now there are rarely actual rounds before the a round, or leads for them",
+"now startups simply raise money from investors one at a time till they feel they have enough",
+"the spectral signature of all mediocre investors",
+"have multiple plans",
+"many investors will ask how much you're planning to raise",
+"this question makes founders feel they should be planning to raise a specific amount",
+"but in fact you shouldn't",
+"it's a mistake to have fixed plans in an undertaking as unpredictable as fundraising",
+"\" i've known a handful of founders who could pull that off without having vcs laugh in their faces",
+"different plans match different investors",
+"$15k per month is high, so don't actually spend that much",
+"but it's ok to use a high estimate when fundraising to add a margin for error",
+"if you have additional expenses, like manufacturing, add in those at the end",
+"underestimate how much you want",
+"then when you reach $150k you're more than half done",
+"whereas if you'd said you were raising $500k, you'd be less than a third done at $150k",
+"if fundraising stalled there for an appreciable time, you'd start to read as a failure",
+"saying initially that you're raising $250k doesn't limit you to raising that much",
+"startups do that all the time",
+"i'm not saying you should lie, but that you should lower your expectations initially",
+"there is almost no downside in starting with a low number",
+"it not only won't cap the amount you raise, but will on the whole tend to increase it",
+"a good metaphor here is angle of attack",
+"if you try to fly at too steep an angle of attack, you just stall",
+"be profitable if you can",
+"if you can make it to profitability without raising any additional money",
+"there are many analogies between fundraising and dating, and this is one of the strongest",
+"no one wants you if you seem desperate",
+"and the best way not to seem desperate is not to be desperate",
+"and they are then surprised how difficult and unpleasant it is",
+"of course not all startups can make it to ramen profitability in a few months",
+"don't optimize for valuation",
+"founders who raise money at high valuations tend to be unduly proud of it",
+"this is stupid, because fundraising is not the test that matters",
+"the real test is revenue",
+"fundraising is just a means to that end",
+"being proud of how well you did at fundraising is like being proud of your college grades",
+"number two is good investors",
+"valuation is at best third",
+"the empirical evidence shows just how unimportant it is",
+"6 million respectively",
+"so let that satisfy your competitiveness",
+"you're doing better than dropbox and airbnb at a test that doesn't matter",
+"it will be easier to raise money at a lower valuation",
+"it shouldn't be, but it is",
+"but although it's a mistake for investors to care about price, a significant number do",
+"yesno before valuation",
+"some investors want to know what your valuation is before they even talk to you about investing",
+"fortunately there is a way to avoid naming a price in this situation",
+"and it is not just a negotiating trick; it's how you (both) should be operating",
+"then if they decide they do want to invest, you can figure out a price",
+"but first things first",
+"this is a safe technique so long as you combine it with the next one",
+"beware \"valuation sensitive\" investors",
+"occasionally you'll encounter investors who describe themselves as \"valuation sensitive",
+"you should therefore never approach such investors first",
+"this way, you'll not only get market price, but it will also take less time",
+"so you'd only want to talk to this sort of investor if you were about to do that anyway",
+"if you're surprised by a lowball offer, treat it as a backup offer and delay responding to it",
+"but lowballing you is a dick move that should be met with the corresponding countermove",
+"accept offers greedily",
+"a greedy algorithm takes the best of the options in front of it right now",
+"and that is how startups should approach fundraising in phases 2 and later",
+"if someone makes you an acceptable offer, take it",
+"if you have multiple incompatible offers, take the best",
+"don't reject an acceptable offer in the hope of getting a better one in the future",
+"these simple rules cover a wide variety of cases",
+"if you're raising money from many investors, roll them up as they say yes",
+"as you start to feel you've raised enough, the threshold for acceptable will start to get higher",
+"in practice offers exist for stretches of time, not points",
+"so when you get an acceptable offer that would be incompatible with others (e",
+"this could lose you some that might have made an offer if they had more time",
+"but by definition you don't care; the initial offer was acceptable",
+"a deadline of three working days is acceptable",
+"you shouldn't need more than that if you've been talking to investors in parallel",
+"but a deadline any shorter is a sign you're dealing with a sketchy investor",
+"you can usually call their bluff, and you may need to",
+"but if it does, \"get the best investors\" is in the average case bad advice",
+"the best investors are also the most selective, because they get their pick of all the startups",
+"(the situation is different in phase 1",
+"there's no practical difficulty",
+"if the smaller investments are on convertible notes, they'll just convert into the series a round",
+"till they do, you don't know for sure they will, and the greedy algorithm tells you what to do",
+"don't sell more than 25% in phase 2",
+"if you do well, you will probably raise a series a round eventually",
+"i say probably because things are changing with series a rounds",
+"startups may start to skip them",
+"which means you should avoid doing things in earlier rounds that will mess up raising an a round",
+"guess conservatively",
+"have one person handle fundraising",
+"(if the founders mistrust one another, this could cause some friction",
+"even if the ceo is a programmer and another founder is a salesperson? yes",
+"but wait till that point",
+"you'll need an executive summary and (maybe) a deck",
+"traditionally phase 2 fundraising consists of presenting a slide deck in person to investors",
+"a lot of the most successful startups we fund never make decks in phase 2",
+"they just talk to investors and explain what they plan to do",
+"but don't refuse on that account to give copies to investors you meet",
+"you just have to treat such leaks as a cost of doing business",
+"in practice it's not that high a cost",
+"i wouldn't do that",
+"it's a sign they're not really interested",
+"stop fundraising when it stops working",
+"when do you stop fundraising? ideally when you've raised enough",
+"but what if you haven't raised as much as you'd like? when do you give up?",
+"when your fundraising options run out, they usually run out in the same way",
+"don't keep sucking on the straw if you're just getting air",
+"it's not going to get better",
+"don't get addicted to fundraising",
+"the work at an early stage startup often consists of unglamorous schleps",
+"whereas fundraising, when it's going well, can be quite the opposite",
+"the danger of fundraising is particularly acute for people who are good at it",
+"it's always fun to work on something you're good at",
+"if you're one of these people, beware",
+"fundraising is not what will make your company successful",
+"listening to users complain about bugs in your software is what will make you successful",
+"startups can be destroyed by this",
+"don't raise too much",
+"though only a handful of startups have to worry about this, it is possible to raise too much",
+"the dangers of raising too much are subtle but insidious",
+"one is that it will set impossibly high expectations",
+"a company's valuation is expected to rise each time it raises money",
+"if not it's a sign of a company in trouble, which makes you unattractive to investors",
+"and you have to be doing really, really well to raise money at $50 million",
+"but the money itself may be more dangerous than the valuation",
+"so if you do raise a huge amount of money, don't spend it",
+"startups raising money occasionally alienate investors by seeming arrogant",
+"it's a mistake to behave arrogantly to investors",
+"the only safe strategy is never to seem arrogant at all",
+"so you must cushion the blow with soft words",
+"at yc we tell startups they can blame us",
+"and now that i've written this, everyone else can blame me if they want",
+"the danger of behaving arrogantly is greatest when you're doing well",
+"when everyone wants you, it's hard not to let it go to your head",
+"especially if till recently no one wanted you",
+"but restrain yourself",
+"the startup world is a small place, and startups have lots of ups and downs",
+"this is a domain where it's more true than usual that pride goeth before a fall",
+"be nice when investors reject you as well",
+"the best investors are not wedded to their initial opinion of you",
+"if they reject you in phase 2 and you end up doing well, they'll often invest in phase 3",
+"in fact investors who reject you are some of your warmest leads for future fundraising",
+"any investor who spent significant time deciding probably came close to saying yes",
+"the bar will be higher next time",
+"assume the money you raise in phase 2 will be the last you ever raise",
+"you must make it to profitability on this money if you can",
+"this is probably the optimal strategy for investors",
+"it's too hard to pick winners early on",
+"better to let the market do it for you",
+"but it often comes as a surprise to startups how much harder it is to raise money in phase 3",
+"the next time you raise money, the experiment has to have worked",
+"you have to be on a trajectory that leads to going public",
+"and while there are some ideas where the proof that the experiment worked might consist of e",
+"query response times, usually the proof is profitability",
+"usually phase 3 fundraising has to be type a fundraising",
+"in practice there are two ways startups hose themselves between phases 2 and 3",
+"some are just too slow to become profitable",
+"they raise enough money to last for two years",
+"there doesn't seem any particular urgency to be profitable",
+"so they don't make any effort to make money for a year",
+"but by that time, not making money has become habitual",
+"when they finally decide to try, they find they can't",
+"the other way companies hose themselves is by letting their expenses grow too fast",
+"which almost always means hiring too many people",
+"you usually shouldn't go out and hire 8 people as soon as you raise money at phase 2",
+"usually you want to wait till you have growth (and thus usually revenues) to justify them",
+"a lot of vcs will encourage you to hire aggressively",
+"don't listen to them",
+"don't make things complicated",
+"that's fundraising in one sentence",
+"don't introduce complicated optimizations, and don't let investors introduce complications either",
+"fundraising is not what will make you successful",
+"it's just a means to an end",
+"be good, take care of yourselves, and don't leave the path",
+"the biggest component in most investors' opinion of you is the opinion of other investors",
+"which is of course a recipe for exponential growth",
+"but actually the two are not that highly correlated",
+"if you understand them, you can at least avoid being surprised",
+"raising money decreases the risk of failure",
+"plus a company that has raised money is literally more valuable",
+"though they're often clueless about technology, most investors are pretty good at reading people",
+"when fundraising is going well, investors are quick to sense it in your increased confidence",
+"judging startups is hard even for the best investors",
+"the mediocre ones might as well be flipping coins",
+"the best investors aren't influenced much by the opinion of other investors",
+"it would only dilute their own judgment to average it together with other people's",
+"this is the fourth way in which offers beget offers",
+"founders try this sort of thing all the time, and investors are very sensitive to it",
+"if anything oversensitive",
+"but you're safe so long as you're telling the truth",
+"there's no manipulation in that",
+"do not, however, tell a who b is",
+"vcs will sometimes ask which other vcs you're talking to, but you should never tell them",
+"angels you can sometimes tell about other angels, because angels cooperate more with one another",
+"the second will be easier",
+"the right way to lift heavy things is to let your legs do the work",
+"inexperienced founders make the same mistake when trying to convince investors",
+"they try to convince with their pitch",
+"investors are looking for startups that will be very successful",
+"but that test is not as simple as it sounds",
+"the big successes are so big they dwarf the rest",
+"but angel investors like big successes too",
+"the most important ingredient is formidable founders",
+"[2] every startup has reasons both to invest and not to invest",
+"if investors think you're a winner they focus on the former, and if not they focus on the latter",
+"for example, it might be a rich market, but with a slow sales cycle",
+"they're not necessarily trying to mislead you",
+"most investors are genuinely unclear in their own minds why they like or dislike startups",
+"if you seem like a winner, they'll like your idea more",
+"but don't be too smug about this weakness of theirs, because you have it too; almost everyone does",
+"there is a role for ideas of course",
+"they're fuel for the fire that starts with liking the founders",
+"\" (whereas when they don't like you, they'll be saying \"but what about x?\")",
+"formidable is close to confident, except that someone could be confident and mistaken",
+"formidable is roughly justifiably confident",
+"what should they do? [4]",
+"what they should not do is try to imitate the swagger of more experienced founders",
+"investors are not always that good at judging technology, but they're good at judging confidence",
+"if you try to act like something you're not, you'll just end up in an uncanny valley",
+"you'll depart from sincere, but never arrive at convincing",
+"the way to seem most formidable as an inexperienced founder is to stick to the truth",
+"how formidable you seem isn't a constant",
+"it varies depending on what you're saying",
+"that's the secret",
+"and by convince yourself, i don't mean play mind games with yourself to boost your confidence",
+"i mean truly evaluate whether your startup is worth investing in",
+"if it isn't, don't try to raise money",
+"to evaluate whether your startup is worth investing in, you have to be a domain expert",
+"which in fact it will usually be",
+"know everything about your market",
+"when the unfortunate fellow got to his last slide, the professor burst out:",
+"which one of these conclusions do you actually believe?",
+"even if you have no ideas",
+"you have to produce something",
+"and all too many startups go into fundraising in the same spirit",
+"it's when you can convince investors, and not before",
+"if you try convincing investors before you've convinced yourself, you'll be wasting both your time",
+"but pausing first to convince yourself will do more than save you from wasting your time",
+"it will force you to organize your thoughts",
+"and if you can do that you'll end up with more than added confidence",
+"you'll also have a provisional roadmap of how to succeed",
+"no one knows whether a startup is going to succeed",
+"startup investors know that every investment is a bet, and against pretty long odds",
+"founders think of startups as ideas, but investors think of them as markets",
+"your target market has to be big, and it also has to be capturable by you",
+"but the market doesn't have to be big yet, nor do you necessarily have to be in it yet",
+"the standard of plausibility varies dramatically depending on the age of the startup",
+"microsoft for example was not going to grow huge selling basic interpreters",
+"good, but not great",
+"no company, however successful, ever looks more than a pretty good bet a few months in",
+"microcomputers turned out to be a big deal, and microsoft both executed well and got lucky",
+"but it was by no means obvious that this was how things would play out",
+"plenty of companies seem as good a bet a few months in",
+"and who can reasonably expect more of a startup than that?",
+"if you can make as good a case as microsoft could have, will you convince investors? not always",
+"a lot of vcs would have rejected microsoft",
+"[9] certainly some rejected google",
+"this is arguably a permissible tactic",
+"it's arguably an instance of scamming a scammer",
+"if you know you're on the right track, then you also know why investors were wrong to reject you",
+"experienced investors are well aware that the best ideas are also the scariest",
+"they all know about the vcs who rejected google",
+"that's what happened to dropbox",
+"yet another backup and syncing thing, they all thought",
+"a couple weeks later, dropbox raised a series a round from sequoia",
+"you can convince yourself, then convince them",
+"and when you convince them, use the same matter-of-fact language you used to convince yourself",
+"you wouldn't use vague, grandiose marketing-speak among yourselves",
+"don't use it with investors either",
+"it not only doesn't work on them, but seems a mark of incompetence",
+"just be concise",
+"so here's the recipe for impressing investors when you're not already good at seeming formidable:",
+"make something worth investing in",
+"understand why it's worth investing in",
+"explain that clearly to investors",
+"if you're saying something you know is true, you'll seem confident when you're saying it",
+"conversely, never let pitching draw you into bullshitting",
+"as long as you stay on the territory of truth, you're strong",
+"make the truth good, then just tell it",
+"one of the most common types of advice we give at y combinator is to do things that don't scale",
+"a lot of would-be founders believe that startups either take off or don't",
+"or they don't, in which case the market must not exist",
+"actually startups take off because the founders make them take off",
+"a good metaphor would be the cranks that car engines had before they got electric starters",
+"the most common unscalable thing founders have to do at the start is to recruit users manually",
+"nearly all startups have to",
+"you can't wait for users to come to you",
+"you have to go out and get them",
+"if anyone could have sat back and waited for users, it was stripe",
+"but in fact they're famous within yc for aggressive early user acquisition",
+"at yc we use the term \"collison installation\" for the technique they invented",
+"\" but the collison brothers weren't going to wait",
+"there are two reasons founders resist going out and recruiting users individually",
+"one is a combination of shyness and laziness",
+"the other reason founders ignore this path is that the absolute numbers seem so small at first",
+"this can't be how the big, famous startups got started, they think",
+"the mistake they make is to underestimate the power of compound growth",
+"we encourage every startup to measure their progress by weekly growth rate",
+"if you have 100 users, you need to get 10 more next week to grow 10% a week",
+"after a year you'll have 14,000 users, and after 2 years you'll have 2 million",
+"airbnb is a classic example of this technique",
+"marketplaces are so hard to get rolling that you should expect to take heroic measures at first",
+"that initial fragility was not a unique feature of airbnb",
+"almost all startups are fragile initially",
+"they unconsciously judge larval startups by the standards of established ones",
+"it's harmless if reporters and know-it-alls dismiss your startup",
+"they always get things wrong",
+"it's even ok if investors dismiss your startup; they'll change their minds when they see growth",
+"the big danger is that you'll dismiss your startup yourself",
+"i've seen it happen",
+"i often have to encourage founders who don't see the full potential of what they're building",
+"even bill gates made that mistake",
+"he returned to harvard for the fall semester after starting microsoft",
+"they were just trying to survive",
+"but in retrospect that too was the optimal path to dominating a big market",
+"otherwise you'll have to make a more deliberate effort to locate the most promising vein of users",
+"you should take extraordinary measures not just to acquire users, but also to make them happy",
+"your first users should feel that signing up with you was one of the best choices they ever made",
+"and you in turn should be racking your brains to think of new ways to delight them",
+"you can be ornery when you're scotty, but not when you're kirk",
+"that would be a great problem to have",
+"see if you can make it happen",
+"tim cook doesn't send you a hand-written note after you buy a laptop",
+"but you can",
+"that's one advantage of being small: you can provide a level of service no big company can",
+"steve wasn't just using \"insanely\" as a synonym for \"very",
+"what novice founders don't get is what insanely great translates to in a larval startup",
+"when steve jobs started using that phrase, apple was already an established company",
+"that's not hard for engineers to grasp",
+"it's just a more extreme version of designing a robust and elegant product",
+"it's not the product that should be insanely great, but the experience of being your user",
+"the product is just one component of that",
+"for a big company it's necessarily the dominant one",
+"can, perhaps, but should? yes",
+"over-engaging with early users is not just a permissible technique for getting growth rolling",
+"making a better mousetrap is not an atomic operation",
+"the feedback you get from engaging directly with your earliest users will be the best you ever get",
+"sometimes the right unscalable trick is to focus on a deliberately narrow market",
+"it's like keeping a fire contained at first to get it really hot before adding more logs",
+"that's what facebook did",
+"at first it was just for harvard students",
+"most startups that use the contained fire strategy do it unconsciously",
+"the strategy works just as well if you do it unconsciously",
+"among companies, the best early adopters are usually other startups",
+"plus when they succeed they grow fast, and you with them",
+"they got started by doing something that really doesn't scale: assembling their routers themselves",
+"hardware startups face an obstacle that software startups don't",
+"the minimum order for a factory production run is usually several hundred thousand dollars",
+"the arrival of crowdfunding (or more precisely, preorders) has helped a lot",
+"but even so i'd advise startups to pull a meraki initially if they can",
+"that's what pebble did",
+"the pebbles assembled the first several hundred watches themselves",
+"\" who knew?",
+"even if there aren't many of them, there are probably adjacent territories that have more",
+"consulting is the canonical example of work that doesn't scale",
+"that's where companies cross the line",
+"we did that at viaweb",
+"since we would do anything to get users, we did",
+"we felt pretty lame at the time",
+"there's a more extreme variant where you don't just use your software, but are your software",
+"some startups could be entirely manual at first",
+"i should mention one sort of initial tactic that usually doesn't work: the big launch",
+"they want to launch simultaneously in 8 different publications, with embargoes",
+"and on a tuesday, of course, since they read somewhere that's the optimum day to launch something",
+"it's easy to see how little launches matter",
+"think of some successful startups",
+"so why do founders think launches matter? a combination of solipsism and laziness",
+"partnerships too usually don't work",
+"it's not enough just to do something extraordinary initially",
+"you have to make an extraordinary effort initially",
+"y combinator has now funded 564 startups including the current batch, which has 53",
+"7 billion, and the 511 prior to the current batch have collectively raised about $1",
+"as usual those numbers are dominated by a few big winners",
+"the top 10 startups account for 8",
+"6 of that 11",
+"but there is a peloton of younger startups behind them",
+"there are about 40 more that have a shot at being really big",
+"i'd guess we can grow another 2 or 3x before hitting the next bottleneck",
+"one consequence of funding such a large number of startups is that we see trends early",
+"i'm going to take a shot at describing where these trends are leading",
+"i think more",
+"now there's a third: start your own company",
+"that's a big change",
+"i think we're still at the beginning of this one",
+"it's hard to predict how big a deal it will be",
+"as big a deal as the industrial revolution? maybe",
+"probably not",
+"one thing we can say for sure is that there will be a lot more startups",
+"this process is not just something happening now in silicon valley",
+"it started decades ago, and it's happening as far afield as the car industry",
+"it has a long way to run",
+"the other big driver of change is that startups are becoming cheaper to start",
+"which means investors will get less stock and less control",
+"there are still a lot of people who'd make great founders who never end up starting a company",
+"you can see that from how randomly some of the most successful startups got started",
+"there might be 10x or even 50x more good founders out there",
+"high returns don't come from investing at low valuations",
+"they come from investing in the companies that do really well",
+"so if there are more of those to be had each year, the best pickers should have more hits",
+"this means there should be more variability in the vc business",
+"whereas the bad firms will get the leftovers, as they do now, and yet pay a higher price for them",
+"nor do i think it will be a problem that founders keep control of their companies for longer",
+"what about angels? i think there is a lot of opportunity there",
+"it used to suck to be an angel investor",
+"and the days when vcs could wash angels out of the cap table are long gone",
+"few investors understand the cost that raising money from them imposes on startups",
+"and in this context, low-cost means deciding quickly",
+"one is that the scariness of starting a startup in the old days was a pretty effective filter",
+"now that the cost of failing is becoming lower, we should expect founders to do it more",
+"that's not a bad thing",
+"it will be interesting, in a bad way, if idea clashes become a lot more common",
+"what used to be an obelisk will become a pyramid",
+"it will be a little wider at the top, but a lot wider at the bottom",
+"imagine the obelisk of investors that corresponds to the obelisk of startups",
+"i think the biggest danger for vcs, and also the biggest opportunity, is at the series a stage",
+"right now, vcs often knowingly invest too much money at the series a stage",
+"some vcs lie and claim the company really needs that much",
+"like a lot of bad things, this didn't happen intentionally",
+"the vc business backed into it as their initial assumptions gradually became obsolete",
+"what will happen to the vc business when that happens? hell if i know",
+"but i bet that particular firm will end up ahead",
+"and that's where the money is",
+"you can't fight market forces forever",
+"40% used to be common",
+"now vcs are fighting to hold the line at 20%",
+"but i am daily waiting for the line to collapse",
+"it's going to happen",
+"you may as well anticipate it, and look bold",
+"who knows, maybe vcs will make more money by doing the right thing",
+"it wouldn't be the first time that happened",
+"venture capital is a business where occasional big successes generate hundredfold returns",
+"if you want to find new opportunities for investing, look for things founders complain about",
+"founders are your customers, and the things they complain about are unsatisfied demand",
+"but the more general recipe is: do something founders want",
+"the way to get startup ideas is not to try to think of startup ideas",
+"it's to look for problems, preferably problems you have yourself",
+"microsoft, apple, yahoo, google, and facebook all began this way",
+"it sounds obvious to say you should only work on problems that exist",
+"and yet by far the most common mistake startups make is to solve problems no one has",
+"i made it myself",
+"in 1995 i started a company to put art galleries online",
+"but galleries didn't want to be online",
+"it's not how the art business works",
+"so why did i spend 6 months working on this stupid idea? because i didn't pay attention to users",
+"i invented a model of the world that didn't correspond to reality, and worked from that",
+"i didn't notice my model was wrong until i tried to convince users to pay for what we'd built",
+"even then i took embarrassingly long to catch on",
+"i was attached to my model of the world, and i'd spent a lot of time on the software",
+"they had to want it",
+"at yc we call these \"made-up\" or \"sitcom\" startup ideas",
+"imagine one of the characters on a tv show was starting a startup",
+"the writers would have to invent something for it to do",
+"but coming up with good startup ideas is hard",
+"it's not something you can do for the asking",
+"for example, a social network for pet owners",
+"it doesn't sound obviously mistaken",
+"millions of people have pets",
+"often they care a lot about their pets and spend a lot of money on them",
+"surely many of these people would like a site where they could talk to other pet owners",
+"you could serve them targeted offers, and maybe charge for premium features",
+"\" they say \"yeah, maybe i could see using something like that",
+"\" even when the startup launches, it will sound plausible to a lot of people",
+"sum that reaction across the entire population, and you have zero users",
+"choose the latter",
+"if you invert the scale on the y axis, you can envision companies as holes",
+"google is an immense crater: hundreds of millions of people use it, and they need it a lot",
+"a startup just starting out can't expect to excavate that much volume",
+"so you have two choices about the shape of hole you start with",
+"you can either dig a hole that's broad but shallow, or one that's narrow and deep, like a well",
+"made-up startup ideas are usually of the first type",
+"lots of people are mildly interested in a social network for pet owners",
+"nearly all good startup ideas are of the second type",
+"microsoft was a well when they made altair basic",
+"thirty years later facebook had the same shape",
+"you don't need the narrowness of the well per se",
+"it's depth you need; you get narrowness as a byproduct of optimizing for depth (and speed)",
+"but you almost always do get it",
+"facebook was a good idea because it started with a small market there was a fast path out of",
+"so you spread rapidly through all the colleges",
+"once you have all the college students, you get everyone else simply by letting them in",
+"the founders of airbnb didn't realize at first how big a market they were tapping",
+"initially they had a much narrower idea",
+"they were going to let hosts rent out space on their floors during conventions",
+"they didn't foresee the expansion of this idea; it forced itself upon them gradually",
+"all they knew at first is that they were onto something",
+"that's probably as much as bill gates or mark zuckerberg knew at first",
+"occasionally it's obvious from the beginning when there's a path out of the initial niche",
+"and sometimes i can see a path that's not immediately obvious; that's one of our specialties at yc",
+"but there are limits to how well this can be done, no matter how much experience you have",
+"in zen and the art of motorcycle maintenance, robert pirsig says:",
+"you want to know how to paint a perfect painting? it's easy",
+"make yourself perfect and then just paint naturally",
+"i've wondered about that passage since i read it in high school",
+"i'm not sure how useful his advice is for painting specifically, but it fits this situation well",
+"empirically, the way to have good startup ideas is to become the sort of person who has them",
+"you can also be at the leading edge as a user",
+"but mark already lived online; to him it seemed natural",
+"paul buchheit says that people at the leading edge of a rapidly changing field \"live in the future",
+"\" combine that with pirsig and you get:",
+"live in the future, then build what's missing",
+"that describes the way many if not most of the biggest startups got started",
+"neither apple nor yahoo nor google nor facebook were even supposed to be companies at first",
+"they grew out of things their founders built because there seemed a gap in the world",
+"\" lots of people heard about the altair",
+"lots forgot usb sticks",
+"the verb you want to be using with respect to startup ideas is not \"think up\" but \"notice",
+"the most successful startups almost all begin this way",
+"that may not have been what you wanted to hear",
+"but disappointing though it may be, this is the truth",
+"and it is a recipe of a sort, just one that in the worst case takes a year rather than a weekend",
+"if you're not at the leading edge of some rapidly changing field, you can get to one",
+"for example, anyone reasonably smart can probably get to an edge of programming (e",
+"building mobile apps) in a year",
+"especially if you're also looking for a cofounder",
+"you don't have to learn programming to be at the leading edge of a domain that's changing fast",
+"other domains change fast",
+"but while learning to hack is not necessary, it is for the forseeable future sufficient",
+"as marc andreessen put it, software is eating the world, and this trend has decades left to run",
+"knowing how to hack also means that when you have ideas, you'll be able to implement them",
+"that's not absolutely necessary (jeff bezos couldn't) but it's an advantage",
+"i'll try building an initial version tonight",
+"what won't be obvious is that they're startup ideas",
+"most things that are missing will take some time to see",
+"you almost have to trick yourself into seeing the ideas around you",
+"but you know the ideas are out there",
+"this is not one of those problems where there might not be an answer",
+"it's impossibly unlikely that this is the exact moment when technological progress stops",
+"and when these problems get solved, they will probably seem flamingly obvious in retrospect",
+"what you need to do is turn off the filters that usually prevent you from seeing them",
+"the most powerful is simply taking the current state of the world for granted",
+"even the most radically open-minded of us mostly do that",
+"you couldn't get from your bed to the front door if you stopped to question everything",
+"pay particular attention to things that chafe you",
+"when something annoys you, it could be because you're living in the future",
+"it was obvious to us as programmers that these sites would have to be generated by software",
+"to sit down and try to think of ideas",
+"give yourself some time",
+"drew houston did work on a less promising idea before dropbox: an sat prep startup",
+"but dropbox was a much better idea, both in the absolute sense and also as a match for his skills",
+"if you do that, you'll naturally tend to build things that are missing",
+"it wouldn't seem as interesting to build something that already existed",
+"it's cool; users love it; it just doesn't matter",
+"microcomputers seemed like toys when apple and microsoft started working on them",
+"\" backrub seemed like an inconsequential science project",
+"the facebook was just a way for undergrads to stalk one another",
+"to us that's positive evidence an idea is good",
+"live in the future and build what seems interesting",
+"that's what i'd advise college students to do, rather than trying to learn about \"entrepreneurship",
+"\" \"entrepreneurship\" is something you learn best by doing it",
+"the examples of the most successful founders make that clear",
+"what you should be spending your time on in college is ratcheting yourself into the future",
+"college is an incomparable opportunity to do that",
+"all you'll learn is the words for things",
+"the clash of domains is a particularly fruitful source of ideas",
+"or better still, go work for a biotech company",
+"cs majors normally get summer jobs at computer hardware or software companies",
+"or don't take any extra classes, and just build things",
+"it's no coincidence that microsoft and facebook both got started in january",
+"but don't feel like you have to build things that will become startups",
+"that's premature optimization",
+"just build things",
+"preferably with other students",
+"you're also surrounded by other people trying to do the same thing",
+"beware of research",
+"whereas a phd dissertation is extremely unlikely to",
+"competition",
+"because a good idea should seem obvious, when you have one you'll tend to feel that you're late",
+"don't let that deter you",
+"worrying that you're late is one of the signs of a good idea",
+"ten minutes of searching the web will usually settle the question",
+"even if you find someone else working on the same thing, you're probably not too late",
+"if you're uncertain, ask users",
+"the question then is whether that beachhead is big enough",
+"err on the side of doing things where you'll face competitors",
+"inexperienced founders usually give competitors more credit than they deserve",
+"whether you succeed depends far more on you than on your competitors",
+"so better a good idea with competitors than a bad one without",
+"in fact that's a very promising starting point",
+"google was that type of idea",
+"your thesis has to be more precise than \"we're going to make an x that doesn't suck\" though",
+"you have to be able to phrase it in terms of something the incumbents are overlooking",
+"google was that type of idea too",
+"they'd prefer not to deal with tedious problems or get involved in messy ways with the real world",
+"which is a reasonable preference, because such things slow you down",
+"and dealing with payments is a schlep for stripe, but not an intolerable one",
+"we overcame this one to work on viaweb",
+"we could see the problem was one that needed to be solved though",
+"and even to the degree it isn't, it's a worse form of self-indulgence",
+"starting a successful startup is going to be fairly laborious no matter what",
+"the unsexy filter, while still a source of error, is not as entirely useless as the schlep filter",
+"particularly as you get older and more experienced",
+"plus if you find an idea sexy, you'll work on it more enthusiastically",
+"sometimes you need an idea now",
+"for example, if you're working on a startup and your initial idea turns out to be bad",
+"for the rest of this essay i'll talk about tricks for coming up with startup ideas on demand",
+"although empirically you're better off using the organic strategy, you could succeed this way",
+"you just have to be more disciplined",
+"you'll see a lot more ideas, most of them bad, so you need to be able to filter them",
+"one of the biggest dangers of not using the organic method is the example of the organic method",
+"organic ideas feel like inspirations",
+"when searching for ideas, look in areas where you have some expertise",
+"if you're a database expert, don't build a chat app for teenagers (unless you're also a teenager)",
+"maybe it's a good idea, but you can't trust your judgment about that, so ignore it",
+"there have to be other ideas that involve databases, and whose quality you can judge",
+"the place to start looking for ideas is things you need",
+"there must be things you need",
+"\" if you can think of any x people said that about, you probably have an idea",
+"you know there's demand, and people don't say that about things that are impossible to build",
+"you're probably not the only one",
+"it's especially good if you're different in a way people will increasingly be",
+"if you're changing ideas, one unusual thing about you is the idea you'd previously been working on",
+"did you discover any needs while working on it? several well-known startups began this way",
+"a particularly promising way to be unusual is to be young",
+"some of the most valuable new ideas take root first among people in their teens and early twenties",
+"it would have been very hard for someone who wasn't a college student to start facebook",
+"the next best thing to an unmet need of your own is an unmet need of someone else",
+"try talking to everyone you can about the gaps they find in the world",
+"you're just looking for something to spark a thought",
+"when you find an unmet need that isn't your own, it may be somewhat blurry at first",
+"the person who needs something may not know exactly what they need",
+"one way to ensure you do a good job solving other people's problems is to make them your own",
+"that may seem like taking things to extremes, but startups are extreme",
+"we love it when founders do such things",
+"don't try to start twitter",
+"those ideas are so rare that you can't find them by looking for them",
+"make something unsexy that people will pay you for",
+"what would you pay for right now?",
+"for example, journalism is in free fall at the moment",
+"but there may still be money to be made from something like journalism",
+"but imagine asking that in the future, not now",
+"when one company or industry replaces another, it usually comes in from the side",
+"and be imaginative about the axis along which the replacement occurs",
+"it could be replaced on any of these axes (it has already started to be on most)",
+"the prices of gene sequencing and 3d printing are both experiencing moore's law-like declines",
+"looking for waves is essentially a way to simulate the organic method",
+"finding startup ideas is a subtle business, and that's why most people who try fail so miserably",
+"it doesn't work well simply to try to think of startup ideas",
+"if you do that, you get bad ones that sound dangerously plausible",
+"but even then, not immediately",
+"it takes time to come across situations where you notice something missing",
+"live in the future and build what seems interesting",
+"strange as it sounds, that's the real recipe",
+"one advantage of y combinator's early, broad focus is that we see trends before most other people",
+"and one of the most conspicuous trends in the last batch was the large number of hardware startups",
+"out of 84 companies, 7 were making hardware",
+"on the whole they've done better than the companies that weren't",
+"they've faced resistance from investors of course",
+"investors have a deep-seated bias against hardware",
+"but investors' opinions are a trailing indicator",
+"there is no one single force driving this trend",
+"hardware does well on crowdfunding sites",
+"electric motors have improved",
+"wireless connectivity of various types can now be taken for granted",
+"it's getting more straightforward to get things manufactured",
+"retailers are less of a bottleneck as customers increasingly buy online",
+"one question i can answer is why hardware is suddenly cool",
+"it always was cool",
+"physical things are great",
+"they just haven't been as great a way to start a rapidly growing business as software",
+"but that rule may not be permanent",
+"it's not even that old; it only dates from about 1990",
+"maybe the advantage of software will turn out to have been temporary",
+"hackers love to build hardware, and customers love to buy it",
+"it wouldn't be the first time something was a bad idea till it wasn't",
+"and it wouldn't be the first time investors learned that lesson from founders",
+"a startup is a company designed to grow fast",
+"being newly founded does not in itself make a company a startup",
+"\" the only essential thing is growth",
+"everything else we associate with startups follows from growth",
+"if you want to start one it's important to understand that",
+"startups are so hard that you can't be pointed off to the side and hope to succeed",
+"you have to know that growth is what you're after",
+"the good news is, if you get growth, everything else tends to fall into place",
+"which means you can use growth like a compass to make almost every decision you face",
+"millions of companies are started every year in the us",
+"only a tiny fraction are startups",
+"most are service businessesrestaurants, barbershops, plumbers, and so on",
+"these are not startups, except in a few unusual cases",
+"a barbershop isn't designed to grow fast",
+"whereas a search engine, for example, is",
+"when i say startups are designed to grow fast, i mean it in two senses",
+"partly i mean designed in the sense of intended, because most startups fail",
+"that difference is why there's a distinct word, \"startup,\" for companies designed to grow fast",
+"we could just talk about super-successful companies and less successful ones",
+"but in fact startups do have a different sort of dna from other businesses",
+"google is not just a barbershop whose founders were unusually lucky and hard-working",
+"google was different from the beginning",
+"to grow rapidly, you need to make something you can sell to a big market",
+"that's the difference between google and a barbershop",
+"a barbershop doesn't scale",
+"barbershops are doing fine in the (a) department",
+"almost everyone needs their hair cut",
+"the problem for a barbershop, as for any retail establishment, is (b)",
+"a barbershop serves customers in person, and few will travel far for a haircut",
+"and even if they did the barbershop couldn't accomodate them",
+"writing software is a great way to solve (b), but you can still end up constrained in (a)",
+"if you make software to teach english to chinese speakers, however, you're in startup territory",
+"most businesses are tightly constrained in (a) or (b)",
+"the distinctive feature of successful startups is that they're not",
+"it might seem that it would always be better to start a startup than an ordinary business",
+"if you write software to teach tibetan to hungarians, you won't have much competition",
+"the constraints that limit ordinary companies also protect them",
+"that's the tradeoff",
+"if you start a barbershop, you only have to compete with other local barbers",
+"if you start a search engine you have to compete with the whole world",
+"bar  neighborhood is a sufficient idea for a small business",
+"similarly for companies constrained in (a)",
+"your niche both protects and defines you",
+"but that's not how most startups get started",
+"[3] but at the moment when successful startups get started, much of the innovation is unconscious",
+"what's different about successful founders is that they can see different problems",
+"steve wozniak's problem was that he wanted his own computer",
+"that was an unusual problem to have in 1975",
+"but technological change was about to make it a much more common one",
+"google has similar origins",
+"larry page and sergey brin wanted to search the web",
+"that's one connection between startup ideas and technology",
+"rapid change in one area uncovers big, soluble problems in other areas",
+"sometimes the changes are advances, and what they change is solubility",
+"but in google's case the most important change was the growth of the web",
+"what changed there was not solubility but bigness",
+"how fast does a company have to grow to be considered a startup? there's no precise answer to that",
+"\"startup\" is a pole, not a threshold",
+"starting one is at first no more than a declaration of one's ambitions",
+"but at first you have no more than commitment",
+"starting a startup is like being an actor in that respect",
+"\"actor\" too is a pole rather than a threshold",
+"at the beginning of his career, an actor is a waiter who goes to auditions",
+"the growth of a successful startup usually has three phases:",
+"eventually a successful startup will grow into a big company",
+"together these three phases produce an s-curve",
+"the phase whose growth defines the startup is the second one, the ascent",
+"its length and slope determine how big the company will be",
+"the slope is the company's growth rate",
+"if there's one number every founder should always know, it's the company's growth rate",
+"that's the measure of a startup",
+"if you don't know that number, you don't even know if you're doing well or badly",
+"\" that's not a rate",
+"a good growth rate during yc is 5-7% a week",
+"if you can hit 10% a week you're doing exceptionally well",
+"if you can only manage 1%, it's a sign you haven't yet figured out what you're doing",
+"the best thing to measure the growth rate of is revenue",
+"the next best, for startups that aren't charging initially, is active users",
+"the key word here is \"just",
+"\" if they decide to grow at 7% a week and they hit that number, they're successful for that week",
+"there's nothing more they need to do",
+"programmers will recognize what we're doing here",
+"we're turning starting a startup into an optimization problem",
+"you don't have to think about what the program should do, just make it faster",
+"for most programmers this is very satisfying work",
+"judging yourself by weekly growth doesn't mean you can look no more than a week ahead",
+"it's not that you don't think about the future, just that you think about it no more than necessary",
+"in theory this sort of hill-climbing could get a startup into trouble",
+"they could end up on a local maximum",
+"but in practice that never happens",
+"nine times out of ten, sitting around strategizing is just a form of procrastination",
+"whereas founders' intuitions about which hill to climb are usually better than they realize",
+"plus the maxima in the space of startup ideas are not spiky and isolated",
+"most fairly good ideas are adjacent to even better ones",
+"the fascinating thing about optimizing for growth is that it can actually discover startup ideas",
+"you can use the need for growth as a form of evolutionary pressure",
+"there's a parallel here to small businesses",
+"for startups, growth is a constraint much like truth",
+"every successful startup is at least partly a product of the imagination of growth",
+"if we project forward we see why",
+"weeklyyearly",
+"a company that grows at 1% a week will grow 1",
+"7x a year, whereas a company that grows at 5% a week will grow 12",
+"a startup that grows at 5% a week will in 4 years be making $25 million a month",
+"what happens to fast growing startups tends to surprise even the founders",
+"small variations in growth rate produce qualitatively different outcomes",
+"and, strangely enough, it's also why they fail so frequently",
+"for the right peoplee",
+"the young bill gatesthe probability might be 20% or even 50%",
+"so it's not surprising that so many want to take a shot at it",
+"and since the latter is huge the former should be too",
+"this doesn't bother me",
+"it's the same with other high-beta vocations, like being an actor or a novelist",
+"i've long since gotten used to it",
+"but it seems to bother a lot of people, particularly those who've started ordinary businesses",
+"if they stepped back and looked at the whole picture they might be less indignant",
+"if you judge by the median startup, the whole concept of a startup seems like a fraud",
+"but it's a mistake to use the median in a domain with so much variation",
+"the test of any investment is the ratio of return to risk",
+"but that's not the only reason investors like startups",
+"the other way to get returns from an investment is in the form of dividends",
+"the founders can't enrich themselves without also enriching the investors",
+"why do founders want to take the vcs' money? growth, again",
+"the constraint between good ideas and growth operates in both directions",
+"it's not merely that you need a scalable idea to grow",
+"if you have such an idea and don't grow fast enough, competitors will",
+"almost every company needs some amount of funding to get started",
+"but startups often raise money even when they are or could be profitable",
+"fundamentally that's how the most successful startups view fundraising",
+"raising money lets you choose your growth rate",
+"a profitable startup could if it wanted just grow on its own revenues",
+"growing slower might be slightly dangerous, but chances are it wouldn't kill them",
+"pretty much every successful startup will get acquisition offers too",
+"why? what is it about startups that makes other companies want to buy them? [13]",
+"but acquirers have an additional reason to want startups",
+"a rapidly growing company is not merely valuable, but dangerous",
+"if it keeps expanding, it might expand into the acquirer's own territory",
+"most product acquisitions have some component of fear",
+"the combination of founders, investors, and acquirers forms a natural ecosystem",
+"just as our ancestors did to explain the apparently too neat workings of the natural world",
+"but there is no secret cabal making it all work",
+"to anyone who knows mark zuckerberg that is the reductio ad absurdum of the initial assumption",
+"if you want to understand startups, understand growth",
+"growth drives everything in this world",
+"and growth explains why successful startups almost invariably get acquisition offers",
+"to acquirers a fast-growing company is not merely valuable but dangerous too",
+"understanding growth is what starting a startup consists of",
+"you're committing to search for one of the rare ideas that generates rapid growth",
+"because these ideas are so valuable, finding one is hard",
+"the startup is the embodiment of your discoveries so far",
+"a startup founder is in effect an economic research scientist",
+"most don't discover anything that remarkable, but some discover relativity",
+"the first rule i knew intellectually, but didn't really grasp till it happened to us",
+"the total value of the companies we've funded is around 10 billion, give or take a few",
+"but just two companies, dropbox and airbnb, account for about three quarters of it",
+"in startups, the big winners are big to a degree that violates our expectations about variation",
+"that yields all sorts of strange consequences",
+"and yet it's true",
+"[2] you need to do what you know intellectually to be right, even though it feels wrong",
+"it's a constant battle for us",
+"it's hard to make ourselves take enough risks",
+"when you interview a startup and think \"they seem likely to succeed,\" it's hard not to fund them",
+"their chances of succeeding seem small",
+"unfortunately picking winners is harder than that",
+"that's made harder by the fact that the best startup ideas seem at first like bad ideas",
+"so the most successful founders tend to work on ideas that few beside them realize are good",
+"\" the intersection is the sweet spot for startups",
+"this concept is a simple one and yet seeing it as a venn diagram is illuminating",
+"it reminds you that there is an intersectionthat there are good ideas that seem bad",
+"it also reminds you that the vast majority of ideas that seem bad are bad",
+"the fact that the best ideas seem like bad ideas makes it even harder to recognize the big winners",
+"one could have described microsoft and apple in exactly the same terms",
+"harder still",
+"wait, it gets worse",
+"when you pick a big winner, you won't know it for two years",
+"meanwhile, the one thing you can measure is dangerously misleading",
+"but we know that's the wrong metric",
+"except an inverse one",
+"that's the scary thing: fundraising is not merely a useless metric, but positively misleading",
+"the big winners could generate 10,000x returns",
+"it takes a conscious effort not to do that too",
+"but those are the wrong eyes to look through",
+"we can afford to take at least 10x as much risk as demo day investors",
+"and since risk is usually proportionate to reward, if you can afford to take more risk you should",
+"i don't know what fraction of them currently raise more after demo day",
+"[5] but the percentage is certainly way over 30%",
+"and frankly the thought of a 30% success rate at fundraising makes my stomach clench",
+"a demo day where only 30% of the startups were fundable would be a shambles",
+"everyone would agree that yc had jumped the shark",
+"we ourselves would feel that yc had jumped the shark",
+"and yet we'd all be wrong",
+"for better or worse that's never going to be more than a thought experiment",
+"we could never stand it",
+"i can make up all sorts of plausible justifications",
+"it might dilute the value of the alumni network",
+"i'm not a very good speaker",
+"i say \"um\" a lot",
+"sometimes i have to pause when i lose my train of thought",
+"i wish i were a better speaker",
+"but i don't wish i were a better speaker like i wish i were a better writer",
+"having good ideas is most of writing well",
+"i first noticed this at a conference several years ago",
+"there was another speaker who was much better than me",
+"he had all of us roaring with laughter",
+"i seemed awkward and halting by comparison",
+"afterward i put my talk online like i usually do",
+"boy was he good",
+"so i decided i'd pay close attention to what he said, to learn how he did it",
+"after about ten sentences i found myself thinking \"i don't want to be a good speaker",
+"for example, when i give a talk i usually write it out beforehand",
+"but here again there's a tradeoff between smoothness and ideas",
+"all the time you spend practicing a talk, you could instead spend making it better",
+"but i always end up spending most of the time rewriting it instead",
+"every talk i give ends up being given from a manuscript full of things crossed out and rewritten",
+"depending on your audience, there are even worse tradeoffs than these",
+"that's true in writing too of course, but the descent is steeper with talks",
+"any given person is dumber as a member of an audience than as a reader",
+"every audience is an incipient mob, and a good speaker uses that",
+"so are talks useless? they're certainly inferior to the written word as a source of ideas",
+"but that's not all talks are good for",
+"when i go to a talk, it's usually because i'm interested in the speaker",
+"talks are also good at motivating me to do things",
+"it's probably no coincidence that so many famous speakers are described as motivational speakers",
+"that may be what public speaking is really for",
+"it's probably what it was originally for",
+"the emotional reactions you can elicit with a talk can be a powerful force",
+"i wish i could say that force was more often used for good than ill, but i'm not sure",
+"one of the cases he decided was brought by the owner of a food shop",
+"the owner wanted the student to pay for the smells he was enjoying",
+"the student was stealing his smells",
+"it sounds ridiculous to us to treat smells as property",
+"but i can imagine scenarios in which one could charge for smells",
+"imagine we were living on a moon base where we had to buy air by the liter",
+"i could imagine air suppliers adding scents at an extra charge",
+"the reason it seems ridiculous to us to treat smells as property is that it wouldn't work to",
+"it would work on a moon base, though",
+"what counts as property depends on what works to treat as property",
+"and that not only can change, but has changed",
+"but hunter gatherers didn't treat land, for example, as property in the way we do",
+"[2] but we are in the midst of such a change now",
+"but with the arrival of networks, it's as if we've moved to a planet with a breathable atmosphere",
+"data moves like smells now",
+"but building new things takes too long",
+"people should be able to charge for content when it works to charge for content",
+"but by \"works\" i mean something more subtle than \"when they can get away with it",
+"\" i mean when people can charge for content without warping society in order to do it",
+"the crazy legal measures that the labels and studios have been taking have a lot of that flavor",
+"newspapers and magazines are just as screwed, but they are at least declining gracefully",
+"the riaa and mpaa would make us breathe through tubes if they could",
+"ultimately it comes down to common sense",
+"this is where it's helpful to have working democracies and multiple sovereign countries",
+"private property is an extremely useful ideaarguably one of our greatest inventions",
+"so far, each new definition of it has brought us increasing material wealth",
+"[4] it seems reasonable to suppose the newest one will too",
+"in this essay i'm going to demonstrate this phenomenon by describing some",
+"any one of them could make you a billionaire",
+"don't worry, it's not a sign of weakness",
+"arguably it's a sign of sanity",
+"the biggest startup ideas are terrifying",
+"and not just because they'd be a lot of work",
+"she says to him:",
+"here's the thing: if you ever got me, you wouldn't have a clue what to do with me",
+"that's what these ideas say to us",
+"this phenomenon is one of the most important things you can understand about startups",
+"[1] you'd expect big startup ideas to be attractive, but actually they tend to repel you",
+"and that has a bunch of consequences",
+"even the most ambitious people are probably best off approaching them obliquely",
+"a new search engine",
+"the best ideas are just on the right side of impossible",
+"i don't know if this one is possible, but there are signs it might be",
+"that was not a natural move for microsoft",
+"they did it because they were afraid of google, and google was in the search business",
+"microsoft : google :: google : facebook",
+"google used to give me a page of the right answers, fast, with no clutter",
+"and the pages don't have the clean, sparse feel they used to",
+"google search results used to look like the output of a unix utility",
+"now if i accidentally put the cursor in the wrong place, anything might happen",
+"the way to win here is to build the search engine all the hackers use",
+"and for the first time in over a decade the idea of switching seems thinkable to me",
+"feel free to make it excessively hackerish",
+"make it really good for code search, for example",
+"replace email",
+"email was not designed to be used the way we use it now",
+"email is not a messaging protocol",
+"it's a todo list",
+"or rather, my inbox is a todo list, and email is the way things get onto it",
+"but it is a disastrously bad todo list",
+"as a todo list protocol, the new protocol should give more power to the recipient than email does",
+"i want there to be more restrictions on what someone can put on my todo list",
+") when does it have to be done?",
+"this is one of those ideas that's like an irresistible force meeting an immovable object",
+"on one hand, entrenched protocols are impossible to replace",
+"and if email is going to get replaced eventually, why not now?",
+"they're all at the mercy of email too",
+"whatever you build, make it fast",
+"gmail has become painfully slow",
+"gmail is slow because google can't afford to spend a lot on it",
+"but people will pay for this",
+"i'd have no problem paying $50 a month",
+"at least $1000 a month",
+"replace universities",
+"people are all over this idea lately, and i think they're onto something",
+"one could do a lot better for a lot less money",
+"i don't think universities will disappear",
+"they won't be replaced wholesale",
+"they'll just lose the de facto monopoly on certain types of learning that they once had",
+"y combinator itself is arguably one of them",
+"if learning breaks up into many little pieces, credentialling may separate from it",
+"universities seem the place to start",
+"internet drama",
+"hollywood has been slow to embrace the internet",
+"a lot of the reason is the horribleness of cable clients, also known as tvs",
+"our family didn't wait for apple tv",
+"we hated our last tv so much that a few months ago we replaced it with an imac bolted to the wall",
+"more can be stolen by things that are a little more closely related, like games",
+"there are two ways delivery and payment could play out",
+"if that's the way things play out, there will also be a need for such infrastructure companies",
+"the next steve jobs",
+"his answer was simply \"no",
+"\" i already feared that would be the answer",
+"i asked more to see how he'd qualify it",
+"but he didn't qualify it at all",
+"no, there will be no more great new stuff beyond whatever's currently in the pipeline",
+"so if apple's not going to make the next ipad, who is? none of the existing players",
+"so the company that creates the next wave of hardware is probably going to have to be a startup",
+"i realize it sounds preposterously ambitious for a startup to try to become as big as apple",
+"but no more ambitious than it was for apple to become as big as apple, and they did it",
+"steve jobs has shown us what's possible",
+"now steve is gone there's a vacuum we can all feel",
+"if a new company led boldly into the future of hardware, users would follow",
+"the ceo of that company, the next steve jobs,\" might not measure up to steve jobs",
+"but he wouldn't have to",
+"he'd just have to do a better job than samsung and hp and nokia, and that seems pretty doable",
+"bring back moore's law",
+"the last 10 years have reminded us what moore's law actually says",
+"actually what it says is that circuit densities will double every 18 months",
+"it used to seem pedantic to point that out",
+"not any more",
+"intel can no longer give us faster cpus, just more of them",
+"this moore's law is not as good as the old one",
+"there are several ways to approach this problem",
+"and if it's not impossible but simply very hard, it might be worth trying to write it",
+"the expected value would be high even if the chance of succeeding was low",
+"the reason the expected value is so high is web services",
+"and that would in turn mean that you got practically all the users",
+"they'd take most of intel's business",
+"then the programmer still does much of the work of optimization",
+"these people might be your employees, or you might create a marketplace for optimization",
+"i realize how crazy all this sounds",
+"in fact, what i like about this idea is all the different ways in which it's wrong",
+"trying to write the sufficiently smart compiler is by definition a mistake",
+"now that's what i call a startup idea",
+"ongoing diagnosis",
+"for example, in 2004 bill clinton found he was feeling short of breath",
+"it seems reasonable to assume bill clinton has the best medical care available",
+"ditto for cancer",
+"cancer will show up on some sort of radar screen immediately",
+"(of course, what shows up on the radar screen may be different from what we think of now as cancer",
+"for example, a friend of mine once had her brain scanned as part of a study",
+"she was horrified when the doctors running the study discovered what appeared to be a large tumor",
+"after further testing, it turned out to be a harmless cyst",
+"but it cost her a few days of terror",
+"but i think that's just an artifact of current limitations",
+"there is room for a lot of startups here",
+"let me conclude with some tactical advice",
+"don't say, for example, that you're going to replace email",
+"if you do that you raise too many expectations",
+"just say you're building todo-list software",
+"that sounds harmless",
+"people can notice you've replaced email when it's a fait accompli",
+"empirically, the way to do really big things seems to be to start with deceptively small things",
+"empirically, it's not just for other people that you need to start small",
+"you need to for your own sake",
+"neither bill gates nor mark zuckerberg knew at first how big their companies were going to get",
+"all they knew was that they were onto something",
+"you'll be better off if you operate like columbus and just head in a general westerly direction",
+"start with something you know works, and when you expand, expand westward",
+"it felt as if there was some kind of wall between us",
+"i could never quite tell if they understood what i was saying",
+"you won't have to babysit the round to make sure it happens",
+"was there some kind of inverse relation between resourcefulness and being hard to talk to?",
+"you don't have to explain in detail; they'll chase down all the implications",
+"that's the connection",
+"it's conversational resourcefulness",
+"they traversed idea space as gingerly as a very old person traverses the physical world",
+"the unsuccessful founders weren't stupid",
+"they just weren't eager to",
+"so being hard to talk to was not what was killing the unsuccessful startups",
+"it was a sign of an underlying lack of resourcefulness",
+"that's what was killing them",
+"but the most immediate evidence i had that something was amiss was that i couldn't talk to them",
+"there are great startup ideas lying around unexploited right under our noses",
+"one reason we don't see them is a phenomenon i call schlep blindness",
+"schlep was originally a yiddish word but has passed into general use in the us",
+"it means a tedious, unpleasant task",
+"no one likes schleps, but hackers especially dislike them",
+"maybe that's possible, but i haven't seen it",
+"one of the many things we do at y combinator is teach hackers about the inevitability of schleps",
+"no, you can't start a startup by just writing code",
+"i remember going through this realization myself",
+"a company is defined by the schleps it will undertake",
+"and schleps should be dealt with the same way you'd deal with a cold swimming pool: just jump in",
+"the most dangerous thing about our dislike of schleps is that much of it is unconscious",
+"your unconscious won't even let you see ideas that involve painful schleps",
+"that's schlep blindness",
+"the phenomenon isn't limited to startups",
+"their unconscious mind decides for them, shrinking from the work involved",
+"the most striking example i know of schlep blindness is stripe, or rather stripe's idea",
+"thousands of people must have known about this problem",
+"you'd have to make deals with banks",
+"plus there are probably all sorts of regulations to comply with",
+"it's a lot more intimidating to start a startup like this than a recipe site",
+"that scariness makes ambitious ideas doubly valuable",
+"(this is also true of starting a startup generally",
+"maybe that's one reason the most successful startups of all so often have young founders",
+"in practice the founders grow with the problems",
+"but no one seems able to foresee that, not even older, more experienced founders",
+"they don't know how much they can grow, but they also don't know how much they'll need to",
+"older founders only make the first mistake",
+"ignorance can't solve everything though",
+"some ideas so obviously entail alarming schleps that anyone can see them",
+"how do you see ideas like that? the trick i recommend is to take yourself out of the picture",
+"somehow it's as if most places were sprayed with startupicide",
+"i wondered about this for years",
+"a couple weeks ago i finally figured it out",
+"i was framing the question wrong",
+"the problem is not that most towns kill startups",
+"it's that death is the default for startups, and most towns don't save them",
+"startups in other places are just doing what startups naturally do: fail",
+"the real question is, what's saving startups in places like silicon valley? [2]",
+"environment",
+"and what drives them both is the number of startup people around you",
+"it's quite a leap to start a startup",
+"it's an unusual thing to do",
+"but in silicon valley it seems normal",
+"in most places, if you start a startup, people treat you as if you're unemployed",
+"having people around you care about what you're doing is an extraordinarily powerful force",
+"even the most willful people are susceptible to it",
+"he responded so eagerly that for about half a second i found myself considering doing it",
+"in most other cities, the prospect of starting a startup just doesn't seem real",
+"in the valley it's not only real but fashionable",
+"that no doubt causes a lot of people to start startups who shouldn't",
+"but i think that's ok",
+"the second component of the antidote is chance meetings with people who can help you",
+"the reason startups are more likely to make it here is that great things happen to them too",
+"in the valley, lightning has a sign bit",
+"and moreover has advanced views, for 2004, on founders retaining control of their companies",
+"you can't say precisely what the miracle will be, or even for sure that one will happen",
+"i bet this is true even for startups we fund",
+"chance meetings play a role like the role relaxation plays in having ideas",
+"the critical thing in both cases is that they drift just the right amount",
+"the meeting between larry page and sergey brin was a good example",
+"for larry page the most important component of the antidote was sergey brin, and vice versa",
+"the antidote is people",
+"i'm not sure why this is so",
+"a large part of yc's function is to accelerate that process",
+"to make a startup hub, you need a lot of people interested in startups",
+"there are three reasons",
+"the first, obviously, is that if you don't have enough density, the chance meetings don't happen",
+"sean parker was exactly what facebook needed in 2004",
+"this is one of the reasons we fund such a large number of companies, incidentally",
+"in most places the atmosphere pulls you back toward the mean",
+"i flew into the bay area a few days ago",
+"i notice this every time i fly over the valley: somehow you can sense something is going on",
+"obviously you can sense prosperity in how well kept a place looks",
+"but there are different kinds of prosperity",
+"silicon valley doesn't look like boston, or new york, or la, or dc"]
\ No newline at end of file
diff --git a/test/recurrent/rnn/basic.js b/test/recurrent/rnn/basic.js
new file mode 100644
index 000000000..7ed72e225
--- /dev/null
+++ b/test/recurrent/rnn/basic.js
@@ -0,0 +1,326 @@
+import assert from 'assert';
+import RNN from '../../../src/recurrent/rnn';
+import rnnCheck from '../../utilities/rnn-check';
+
+function notZero(v) {
+  return v !== 0;
+}
+
+function isZero(v) {
+  return v === 0;
+}
+
+describe('rnn', () => {
+  describe('basic operations', () => {
+    it('starts with zeros in input.recurrence', () => {
+      (new RNN()).model.input.recurrence.forEach((v) => {
+        assert(v === 0);
+      });
+    });
+    it('after initial run, does not have zeros in recurrence', () => {
+      var net = new RNN({
+        hiddenSizes: [3],
+        inputSize: 3,
+        inputRange: 2,
+        outputSize: 2
+      });
+      net.train([1, 1, 0]);
+      net.model.input.recurrence.forEach((v) => {
+        assert.equal(v, 0);
+      });
+      net.runBackpropagate([1, 1, 0]);
+      net.runBackpropagate([0, 1, 1]);
+      net.runBackpropagate([1, 0, 1]);
+      net.runBackpropagate([1, 1, 0]);
+      assert(net.model.input.recurrence.some(notZero));
+    });
+  });
+  describe('toJSON method', () => {
+    var net = new RNN();
+    var json = net.toJSON();
+  });
+  describe('xor', () => {
+    function xorNet() {
+      return new RNN({
+        hiddenSizes: [3],
+        inputSize: 3,
+        inputRange: 2,
+        outputSize: 3
+      });
+    }
+
+    var xorNetValues = [
+      [0, 0, 0],
+      [0, 1, 1],
+      [1, 0, 1],
+      [1, 1, 0]
+    ];
+
+    it('properly provides values to equations[].run', () => {
+      var net = xorNet();
+      var called = [];
+      net.model.equations[0] = { run: (v) => {
+        called[0] = v;
+        return {rows: 1, columns: 0, weights: [], recurrence: []}; }
+      };
+      net.model.equations[1] = { run: (v) => {
+        called[1] = v;
+        return {rows: 0, columns: 0, weights: [], recurrence: []}; }
+      };
+      net.model.equations[2] = { run: (v) => {
+        called[2] = v;
+        return {rows: 0, columns: 0, weights: [], recurrence: []}; }
+      };
+      net.model.equations[3] = { run: (v) => {
+        called[3] = v;
+        return {rows: 0, columns: 0, weights: [], recurrence: []}; }
+      };
+      net.model.equations[4] = { run: (v) => {
+        called[4] = v;
+        return {rows: 0, columns: 0, weights: [], recurrence: []}; }
+      };
+      net.train([0, 0, 0]);
+      assert.equal(called.length, 4);
+      assert.equal(called[0], 0);
+      assert.equal(called[1], 1);
+      assert.equal(called[2], 1);
+      assert.equal(called[3], 1);
+      net.train([0, 1, 1]);
+      assert.equal(called.length, 4);
+      assert.equal(called[0], 0);
+      assert.equal(called[1], 1);
+      assert.equal(called[2], 2);
+      assert.equal(called[3], 2);
+    });
+
+    it('properly provides values to equations[].runBackpropagate', () => {
+      var net = xorNet();
+      var backPropagateCalled = [];
+      net.model.equations[0] = {
+        run: () => {
+          return {rows: 0, columns: 0, weights: [], recurrence: []};
+        },
+        runBackpropagate: (v) => {
+          backPropagateCalled[0] = v;
+        }
+      };
+      net.model.equations[1] = {
+        run: () => {
+          return {rows: 0, columns: 0, weights: [], recurrence: []};
+        },
+        runBackpropagate: (v) => {
+          backPropagateCalled[1] = v;
+        }
+      };
+      net.model.equations[2] = {
+        run: () => {
+          return {rows: 0, columns: 0, weights: [], recurrence: []};
+        },
+        runBackpropagate: (v) => {
+          backPropagateCalled[2] = v;
+        }
+      };
+      net.model.equations[3] = {
+        run: () => {
+          return {rows: 0, columns: 0, weights: [], recurrence: []};
+        },
+        runBackpropagate: (v) => {
+          backPropagateCalled[3] = v;
+        }
+      };
+      net.train([0, 0, 0]);
+      net.runBackpropagate([0, 0, 0]);
+      assert.equal(backPropagateCalled.length, 4);
+      assert.equal(backPropagateCalled[0], 0);
+      assert.equal(backPropagateCalled[1], 1);
+      assert.equal(backPropagateCalled[2], 1);
+      assert.equal(backPropagateCalled[3], 1);
+      net.train([0, 1, 1]);
+      net.runBackpropagate([0, 1, 1]);
+      assert.equal(backPropagateCalled.length, 4);
+      assert.equal(backPropagateCalled[0], 0);
+      assert.equal(backPropagateCalled[1], 1);
+      assert.equal(backPropagateCalled[2], 2);
+      assert.equal(backPropagateCalled[3], 2);
+    });
+
+    it('properly provides values to equations[].runBackpropagate', () => {
+      var net = xorNet();
+      var backPropagateCalled = [];
+      net.model.equations[0] = {
+        run: () => {
+          return {rows: 0, columns: 0, weights: [], recurrence: []};
+        },
+        runBackpropagate: (v) => {
+          backPropagateCalled[0] = v;
+        }
+      };
+      net.model.equations[1] = {
+        run: () => {
+          return {rows: 0, columns: 0, weights: [], recurrence: []};
+        },
+        runBackpropagate: (v) => {
+          backPropagateCalled[1] = v;
+        }
+      };
+      net.model.equations[2] = {
+        run: () => {
+          return {rows: 0, columns: 0, weights: [], recurrence: []};
+        },
+        runBackpropagate: (v) => {
+          backPropagateCalled[2] = v;
+        }
+      };
+      net.model.equations[3] = {
+        run: () => {
+          return {rows: 0, columns: 0, weights: [], recurrence: []};
+        },
+        runBackpropagate: (v) => {
+          backPropagateCalled[3] = v;
+        }
+      };
+      net.train([0, 0, 0]);
+      net.runBackpropagate([0, 0, 0]);
+      assert.equal(backPropagateCalled.length, 4);
+      assert.equal(backPropagateCalled[0], 0);
+      assert.equal(backPropagateCalled[1], 1);
+      assert.equal(backPropagateCalled[2], 1);
+      assert.equal(backPropagateCalled[3], 1);
+      net.train([0, 1, 1]);
+      net.runBackpropagate([0, 1, 1]);
+      assert.equal(backPropagateCalled.length, 4);
+      assert.equal(backPropagateCalled[0], 0);
+      assert.equal(backPropagateCalled[1], 1);
+      assert.equal(backPropagateCalled[2], 2);
+      assert.equal(backPropagateCalled[3], 2);
+    });
+
+    it('is fully connected and gives values in recurrence', () => {
+      var net = xorNet();
+      var input = xorNetValues[2];
+      net.model.allMatrices.forEach((m) => {
+        m.recurrence.forEach((value) => {
+          assert.equal(value, 0);
+        });
+      });
+      net.train(input);
+
+      net.model.input.recurrence.forEach((v) => {
+        assert.equal(v, 0);
+      });
+      net.model.hiddenLayers.forEach((layer) => {
+        for (var p in layer) {
+          if (!layer.hasOwnProperty(p)) continue;
+          layer[p].recurrence.forEach((v) => {
+            assert.equal(v, 0);
+          });
+        }
+      });
+      net.model.output.recurrence.forEach((v) => {
+        assert.equal(v, 0);
+      });
+
+      net.runBackpropagate(input);
+
+      assert(net.model.input.recurrence.some(notZero));
+      net.model.hiddenLayers.forEach((layer) => {
+        for (var p in layer) {
+          if (!layer.hasOwnProperty(p)) continue;
+          assert(layer[p].recurrence.some(notZero));
+        }
+      });
+      assert(net.model.output.recurrence.some(notZero));
+
+      net.model.equations.forEach((equation) => {
+        equation.states.forEach((state) => {
+          if (state.left && state.left.recurrence) state.left.recurrence.some(notZero);
+          if (state.right && state.right.recurrence) state.right.recurrence.some(notZero);
+          if (state.product && state.product.recurrence) state.product.recurrence.some(notZero);
+        });
+      });
+    });
+
+    it('recurrence is reset to zero after .step() is called', () => {
+      var net = xorNet();
+      var input = xorNetValues[2];
+      net.train(input);
+      net.runBackpropagate(input);
+      net.step();
+
+      assert(net.model.input.recurrence.every(isZero));
+      net.model.hiddenLayers.forEach((layer) => {
+        for (var p in layer) {
+          if (!layer.hasOwnProperty(p)) continue;
+          assert(layer[p].recurrence.every(isZero));
+        }
+      });
+      assert(net.model.output.recurrence.every(isZero));
+
+      net.model.equations.forEach((equation) => {
+        equation.states.forEach((state) => {
+          if (state.left && state.left.recurrence) state.left.recurrence.every(isZero);
+          if (state.right && state.right.recurrence) state.right.recurrence.every(isZero);
+          if (state.product && state.product.recurrence) state.product.recurrence.every(isZero);
+        });
+      });
+    });
+
+    it('recurrence and weights do not explode', () => {
+      var net = xorNet();
+      var input = xorNetValues[2];
+      for (var i = 0; i < 100; i++)
+      {
+        rnnCheck.allMatrices(net.model, (values) => {
+          values.forEach((value, i) => {
+            assert(value < 50 && value > -50);
+          });
+        });
+        net.train(input);
+        rnnCheck.allMatrices(net.model, (values) => {
+          values.forEach((value, i) => {
+            assert(value < 50 && value > -50);
+          });
+        });
+        net.runBackpropagate(input);
+        rnnCheck.allMatrices(net.model, (values) => {
+          values.forEach((value, i) => {
+            assert(value < 50 && value > -50);
+          });
+        });
+        net.step();
+        rnnCheck.allMatrices(net.model, (values) => {
+          values.forEach((value, i) => {
+            assert(value < 50 && value > -50);
+          });
+        });
+      }
+    });
+
+    it('can learn xor (perplexity goes down)', () => {
+      var net = xorNet();
+      var initialPerplexity;
+      var perplexity;
+
+      for (var i = 0; i < 10; i++) {
+        var input = xorNetValues[Math.floor((xorNetValues.length - 1) * Math.random())];
+        net.run(input);
+        perplexity = net.totalPerplexity;
+        if (i === 0) {
+          initialPerplexity = perplexity;
+        }
+        console.log(perplexity);
+      }
+      assert(initialPerplexity > perplexity);
+    });
+
+    it('can predict xor', () => {
+      var net = xorNet();
+      for (var i = 0; i < 200; i++) {
+        var input = xorNetValues[Math.floor((xorNetValues.length - 1) * Math.random())];
+        net.run(input);
+      }
+
+      assert.equal(net.predict().length, 3);
+    });
+  });
+});
\ No newline at end of file
diff --git a/test/recurrent/rnn/json.js b/test/recurrent/rnn/json.js
new file mode 100644
index 000000000..7bf1c39df
--- /dev/null
+++ b/test/recurrent/rnn/json.js
@@ -0,0 +1,52 @@
+import fs from 'fs';
+import assert from 'assert';
+import RNN from '../../../src/recurrent/rnn';
+import Vocab from '../../../src/utilities/vocab';
+var vocab = new Vocab(['0','1','2','3','4','5','6','7','8','9','+','=']);
+
+describe('json', () => {
+  describe('#toJSON', () => {
+    it('can export model as json', () => {
+      var net = new RNN({
+        inputSize: 6,
+        inputRange: 12,
+        outputSize: 6
+      });
+      var json = net.toJSON();
+
+      compare(json.input, net.model.input);
+      net.model.hiddenLayers.forEach((layer, i) => {
+        compare(json.hiddenLayers[i].weight, layer.weight);
+        compare(json.hiddenLayers[i].transition, layer.transition);
+        compare(json.hiddenLayers[i].bias, layer.bias);
+      });
+      compare(json.output, net.model.output);
+      compare(json.outputConnector, net.model.outputConnector);
+
+      function compare(left, right) {
+        left.weights.forEach((value, i) => {
+          assert.equal(value, right.weights[i]);
+        });
+        assert.equal(left.rows, right.rows);
+        assert.equal(left.columns, right.columns);
+      }
+    });
+  });
+
+  describe('#fromJSON', () => {
+    it('can import model from json', () => {
+      var jsonString = JSON.stringify(new RNN({
+        inputSize: 6, //<- length
+        inputRange: vocab.characters.length,
+        outputSize: vocab.characters.length //<- length
+      }).toJSON());
+
+      var clone = new RNN({ json: JSON.parse(jsonString) });
+
+      assert.equal(jsonString, JSON.stringify(clone.toJSON()));
+      assert.equal(clone.inputSize, 6);
+      assert.equal(clone.inputRange, vocab.characters.length);
+      assert.equal(clone.outputSize, vocab.characters.length);
+    });
+  });
+});
\ No newline at end of file
diff --git a/test/recurrent/rnn/to-function.js b/test/recurrent/rnn/to-function.js
new file mode 100644
index 000000000..60d1cab75
--- /dev/null
+++ b/test/recurrent/rnn/to-function.js
@@ -0,0 +1,24 @@
+import assert from 'assert';
+import Vocab from '../../../src/utilities/vocab';
+import RNN from '../../../src/recurrent/rnn';
+
+describe('rnn.toFunction', () => {
+  it('can output same as run method', () => {
+    const vocab = new Vocab(['h', 'i', ' ', 'm', 'o', '!']);
+    var net = new RNN({
+      inputSize: 7,
+      inputRange: vocab.characters.length,
+      outputSize: 7
+    });
+
+    for (var i = 0; i < 100; i++) {
+      net.run(vocab.toIndexes('hi mom!'));
+      if (i % 10) {
+        console.log(vocab.toCharacters(net.predict()).join(''));
+      }
+    }
+
+    var lastOutput = vocab.toCharacters(net.predict()).join('');
+    assert.equal(vocab.toCharacters(net.toFunction()()).join(''), lastOutput);
+  });
+});
\ No newline at end of file
diff --git a/test/utilities/math-addition-vocab.js b/test/utilities/math-addition-vocab.js
new file mode 100644
index 000000000..31d7868ab
--- /dev/null
+++ b/test/utilities/math-addition-vocab.js
@@ -0,0 +1,43 @@
+import Vocab from '../../src/utilities/vocab';
+import shuffle from './shuffle';
+export const vocab = new Vocab(['0','1','2','3','4','5','6','7','8','9','+','=']);
+export function build() {
+  let items = [];
+  for (let i = 0; i < 10; i++) {
+    for (let j = 0; j < 10; j++) {
+      items.push(`${i}+${j}=${ i + j }`);
+      if (i === j) continue;
+      items.push(`${j}+${i}=${ i + j }`);
+    }
+  }
+  items.random = function() {
+    return items[Math.floor(Math.random() * items.length)];
+  };
+  return shuffle(items);
+}
+
+export function train(rnn) {
+  let items = build();
+  for (let i = 0, max = items.length; i < max; i++) {
+    rnn.run(vocab.toIndexes(items[i]));
+    if (i % 10 === 0) {
+      console.log(vocab.toCharacters(rnn.predict()).join(''));
+    }
+  }
+}
+
+export function trainUntil(rnn, fn) {
+  let items = build();
+  var i = 0;
+  while (true) {
+    rnn.run(vocab.toIndexes(items.random()));
+    if (i % 10 === 0) {
+      var preduction = vocab.toCharacters(rnn.predict()).join('');
+      //console.log(preduction);
+      if (fn(preduction)) {
+        break;
+      }
+    }
+    i++;
+  }
+}
\ No newline at end of file
diff --git a/test/utilities/rnn-check.js b/test/utilities/rnn-check.js
new file mode 100644
index 000000000..3b8027084
--- /dev/null
+++ b/test/utilities/rnn-check.js
@@ -0,0 +1,81 @@
+import assert from 'assert';
+
+export function allWeights(model, fn) {
+  fn(model.input.weights);
+  model.hiddenLayers.forEach((layer) => {
+    for (var p in layer) {
+      if (!layer.hasOwnProperty(p)) continue;
+      assert(fn(layer[p].weights));
+    }
+  });
+  fn(model.output.weights);
+
+  model.equations.forEach((equation) => {
+    equation.states.forEach((state) => {
+      if (state.left && state.left.weights) fn(state.left.weights);
+      if (state.right && state.right.weights) fn(state.right.weights);
+      if (state.product && state.product.weights) fn(state.product.weights);
+    });
+  });
+}
+
+export function allRecurrences(model, fn) {
+  fn(model.input.recurrence);
+  model.hiddenLayers.forEach((layer) => {
+    for (var p in layer) {
+      if (!layer.hasOwnProperty(p)) continue;
+      assert(fn(layer[p].recurrence));
+    }
+  });
+  fn(model.output.recurrence);
+
+  model.equations.forEach((equation) => {
+    equation.states.forEach((state) => {
+      if (state.left && state.left.recurrence) fn(state.left.recurrence);
+      if (state.right && state.right.recurrence) fn(state.right.recurrence);
+      if (state.product && state.product.recurrence) fn(state.product.recurrence);
+    });
+  });
+}
+
+export function allMatrices(model, fn) {
+  fn(model.input.weights);
+  model.hiddenLayers.forEach((layer) => {
+    for (var p in layer) {
+      if (!layer.hasOwnProperty(p)) continue;
+      fn(layer[p].weights);
+    }
+  });
+  fn(model.output.weights);
+
+  model.equations.forEach((equation, equationIndex) => {
+    equation.states.forEach((state, stateIndex) => {
+      if (state.left && state.left.weights) fn(state.left.weights);
+      if (state.right && state.right.weights) fn(state.right.weights);
+      if (state.product && state.product.weights) fn(state.product.weights);
+    });
+  });
+
+  fn(model.input.recurrence);
+  model.hiddenLayers.forEach((layer) => {
+    for (var p in layer) {
+      if (!layer.hasOwnProperty(p)) continue;
+      fn(layer[p].recurrence);
+    }
+  });
+  fn(model.output.recurrence);
+
+  model.equations.forEach((equation, equationIndex) => {
+    equation.states.forEach((state, stateIndex) => {
+      if (state.left && state.left.recurrence) fn(state.left.recurrence);
+      if (state.right && state.right.recurrence) fn(state.right.recurrence);
+      if (state.product && state.product.recurrence) fn(state.product.recurrence);
+    });
+  });
+}
+
+export default {
+  allMatrices,
+  allWeights,
+  allRecurrences
+};
\ No newline at end of file
diff --git a/test/utilities/shuffle.js b/test/utilities/shuffle.js
new file mode 100644
index 000000000..8cfbc4da0
--- /dev/null
+++ b/test/utilities/shuffle.js
@@ -0,0 +1,19 @@
+//http://stackoverflow.com/a/2450976/1324039
+export default function shuffle(array) {
+  var currentIndex = array.length, temporaryValue, randomIndex;
+
+  // While there remain elements to shuffle...
+  while (0 !== currentIndex) {
+
+    // Pick a remaining element...
+    randomIndex = Math.floor(Math.random() * currentIndex);
+    currentIndex -= 1;
+
+    // And swap it with the current element.
+    temporaryValue = array[currentIndex];
+    array[currentIndex] = array[randomIndex];
+    array[randomIndex] = temporaryValue;
+  }
+
+  return array;
+}
\ No newline at end of file
diff --git a/test/utilities/vocab.js b/test/utilities/vocab.js
new file mode 100644
index 000000000..e37b1b21c
--- /dev/null
+++ b/test/utilities/vocab.js
@@ -0,0 +1,90 @@
+import assert from 'assert';
+import Vocab from '../../src/utilities/vocab';
+
+describe('vocab', function() {
+  let vocab = new Vocab('abcdefghijklmnopqrstuvwxyz'.split(''));
+  describe('toIndexes', function() {
+    it('does not have zeros', function() {
+      let vocabIndexes = vocab.toIndexes('abcdefghijklmnopqrstuvwxyz'.split(''));
+      assert.equal(vocabIndexes[0], 0);
+      assert.equal(vocabIndexes[1], 1);
+      assert.equal(vocabIndexes[2], 2);
+      assert.equal(vocabIndexes[3], 3);
+      assert.equal(vocabIndexes[4], 4);
+      assert.equal(vocabIndexes[5], 5);
+      assert.equal(vocabIndexes[6], 6);
+      assert.equal(vocabIndexes[7], 7);
+      assert.equal(vocabIndexes[8], 8);
+      assert.equal(vocabIndexes[9], 9);
+      assert.equal(vocabIndexes[10], 10);
+      assert.equal(vocabIndexes[11], 11);
+      assert.equal(vocabIndexes[12], 12);
+      assert.equal(vocabIndexes[13], 13);
+      assert.equal(vocabIndexes[14], 14);
+      assert.equal(vocabIndexes[15], 15);
+      assert.equal(vocabIndexes[16], 16);
+      assert.equal(vocabIndexes[17], 17);
+      assert.equal(vocabIndexes[18], 18);
+      assert.equal(vocabIndexes[19], 19);
+      assert.equal(vocabIndexes[20], 20);
+      assert.equal(vocabIndexes[21], 21);
+      assert.equal(vocabIndexes[22], 22);
+      assert.equal(vocabIndexes[23], 23);
+      assert.equal(vocabIndexes[24], 24);
+      assert.equal(vocabIndexes[25], 25);
+    });
+    it('should properly be able to reference indexes of cat', function() {
+      var vocab = new Vocab(['cat']);
+      var asIndexes = [0, 1, 2];
+      vocab.toIndexes('cat').forEach(function(v, i) {
+        assert(v === asIndexes[i]);
+      });
+    });
+    it('should properly be able to reference indexes of math', function() {
+      var vocab = new Vocab(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '=', '+']);
+      var asIndexes = [0, 11, 8, 10, 8];
+      vocab.toIndexes('0+8=8').forEach(function(v, i) {
+        assert(v === asIndexes[i]);
+      });
+    });
+  });
+  describe('toCharacters', function() {
+    it('does not have zeros', function() {
+      let vocabCharacters = vocab.toCharacters([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]);
+      assert.equal(vocabCharacters[0], 'a');
+      assert.equal(vocabCharacters[1], 'b');
+      assert.equal(vocabCharacters[2], 'c');
+      assert.equal(vocabCharacters[3], 'd');
+      assert.equal(vocabCharacters[4], 'e');
+      assert.equal(vocabCharacters[5], 'f');
+      assert.equal(vocabCharacters[6], 'g');
+      assert.equal(vocabCharacters[7], 'h');
+      assert.equal(vocabCharacters[8], 'i');
+      assert.equal(vocabCharacters[9], 'j');
+      assert.equal(vocabCharacters[10], 'k');
+      assert.equal(vocabCharacters[11], 'l');
+      assert.equal(vocabCharacters[12], 'm');
+      assert.equal(vocabCharacters[13], 'n');
+      assert.equal(vocabCharacters[14], 'o');
+      assert.equal(vocabCharacters[15], 'p');
+      assert.equal(vocabCharacters[16], 'q');
+      assert.equal(vocabCharacters[17], 'r');
+      assert.equal(vocabCharacters[18], 's');
+      assert.equal(vocabCharacters[19], 't');
+      assert.equal(vocabCharacters[20], 'u');
+      assert.equal(vocabCharacters[21], 'v');
+      assert.equal(vocabCharacters[22], 'w');
+      assert.equal(vocabCharacters[23], 'x');
+      assert.equal(vocabCharacters[24], 'y');
+      assert.equal(vocabCharacters[25], 'z');
+    });
+    it('should properly be able to reference characters of cat', function() {
+      var vocab = new Vocab(['cat']);
+      var asIndexes = [0, 1, 2];
+      var asCharacters = 'cat';
+      vocab.toCharacters(asIndexes).forEach(function(v, i) {
+        assert(v === asCharacters[i]);
+      });
+    });
+  });
+});
\ No newline at end of file