[
  {
    "path": ".gitignore",
    "content": "# Mac.\n.DS_STORE\n\n# Node.\nnode_modules\nnpm-debug.log\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "Thanks for taking the time to contribute to brain.js. Follow these guidelines to make the process smoother:\n\n1. One feature per pull request. Each PR should have one focus, and all the code changes should be supporting that one feature or bug fix. Using a [separate branch](https://guides.github.com/introduction/flow/index.html) for each feature should help you manage developing multiple features at once.\n\n2. Follow the style of the file when it comes to syntax like curly braces and indents.\n\n3. Add a test for the feature or fix, if possible. See the `test` directory for existing tests and README describing how to run these tests.\n"
  },
  {
    "path": "Gruntfile.js",
    "content": "/*\n * To run this file:\n *  `npm install --dev`\n *  `npm install -g grunt`\n *\n *  `grunt --help`\n */\n\nvar fs = require(\"fs\"),\n    browserify = require(\"browserify\"),\n    pkg = require(\"./package.json\");\n\nmodule.exports = function(grunt) {\n  grunt.initConfig({\n    mochaTest: {\n      test: {\n        options: {\n          style: 'bdd',\n          reporter: 'spec'\n        },\n        src: ['test/unit/*.js']\n      }\n    },\n    pkg: grunt.file.readJSON('package.json'),\n    uglify: {\n      options: {\n        banner: \"/*\\n\" + grunt.file.read('LICENSE') + \"*/\"\n      },\n      dist: {\n        files: {\n          '<%=pkg.name%>-<%=pkg.version%>.min.js': ['<%=pkg.name%>-<%=pkg.version%>.js']\n        }\n      }\n    }\n  });\n\n  grunt.registerTask('build', 'build a browser file', function() {\n    var done = this.async();\n\n    var outfile = './brain-' + pkg.version + '.js';\n\n    var bundle = browserify('./browser.js').bundle(function(err, src) {\n      console.log(\"> \" + outfile);\n\n      // prepend license\n      var license = fs.readFileSync(\"./LICENSE\");\n      src = \"/*\\n\" + license + \"*/\" + src;\n\n      // write out the browser file\n      fs.writeFileSync(outfile, src);\n      done();\n    });\n  });\n  grunt.registerTask('test', 'mochaTest');\n\n  grunt.loadNpmTasks('grunt-mocha-test');\n  grunt.loadNpmTasks('grunt-contrib-uglify');\n};\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2010 Heather Arthur\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n"
  },
  {
    "path": "README.md",
    "content": "*This project has reached the end of its development as a simple neural network library. Feel free to browse the code, but please use other JavaScript neural network libraries in development like [brain.js](https://github.com/BrainJS/brain.js) and [convnetjs](https://github.com/karpathy/convnetjs).*\n\n# brain\n\n`brain` is a JavaScript [neural network](http://neuralnetworksanddeeplearning.com/) library. Here's an example of using it to approximate the XOR function:\n\n```javascript\nvar net = new brain.NeuralNetwork();\n\nnet.train([{input: [0, 0], output: [0]},\n           {input: [0, 1], output: [1]},\n           {input: [1, 0], output: [1]},\n           {input: [1, 1], output: [0]}]);\n\nvar output = net.run([1, 0]);  // [0.987]\n```\n\nThere's no reason to use a neural network to figure out XOR however (-: so here's a more involved, realistic example:\n[Demo: training a neural network to recognize color contrast](http://harthur.github.com/brain/)\n\n## Using in node\nIf you have [node](http://nodejs.org/) you can install with [npm](http://npmjs.org):\n\n```\nnpm install brain\n```\n\n## Using in the browser\nDownload the latest [brain.js](https://github.com/harthur/brain/tree/gh-pages). Training is computationally expensive, so you should try to train the network offline (or on a Worker) and use the `toFunction()` or `toJSON()` options to plug the pre-trained network in to your website.\n\n## Training\nUse `train()` to train the network with an array of training data. The network has to be trained with all the data in bulk in one call to `train()`. The more training patterns, the longer it will probably take to train, but the better the network will be at classifiying new patterns.\n\n#### Data format\nEach training pattern should have an `input` and an `output`, both of which can be either an array of numbers from `0` to `1` or a hash of numbers from `0` to `1`. For the [color constrast demo](http://harthur.github.com/brain/) it looks something like this:\n\n```javascript\nvar net = new brain.NeuralNetwork();\n\nnet.train([{input: { r: 0.03, g: 0.7, b: 0.5 }, output: { black: 1 }},\n           {input: { r: 0.16, g: 0.09, b: 0.2 }, output: { white: 1 }},\n           {input: { r: 0.5, g: 0.5, b: 1.0 }, output: { white: 1 }}]);\n\nvar output = net.run({ r: 1, g: 0.4, b: 0 });  // { white: 0.99, black: 0.002 }\n```\n\n#### Options\n`train()` takes a hash of options as its second argument:\n\n```javascript\nnet.train(data, {\n  errorThresh: 0.005,  // error threshold to reach\n  iterations: 20000,   // maximum training iterations\n  log: true,           // console.log() progress periodically\n  logPeriod: 10,       // number of iterations between logging\n  learningRate: 0.3    // learning rate\n})\n```\n\nThe network will train until the training error has gone below the threshold (default `0.005`) or the max number of iterations (default `20000`) has been reached, whichever comes first.\n\nBy default training won't let you know how its doing until the end, but set `log` to `true` to get periodic updates on the current training error of the network. The training error should decrease every time. The updates will be printed to console. If you set `log` to a function, this function will be called with the updates instead of printing to the console.\n\nThe learning rate is a parameter that influences how quickly the network trains. It's a number from `0` to `1`. If the learning rate is close to `0` it will take longer to train. If the learning rate is closer to `1` it will train faster but it's in danger of training to a local minimum and performing badly on new data. The default learning rate is `0.3`.\n\n#### Output\nThe output of `train()` is a hash of information about how the training went:\n\n```javascript\n{\n  error: 0.0039139985510105032,  // training error\n  iterations: 406                // training iterations\n}\n```\n\n#### Failing\nIf the network failed to train, the error will be above the error threshold. This could happen because the training data is too noisy (most likely), the network doesn't have enough hidden layers or nodes to handle the complexity of the data, or it hasn't trained for enough iterations.\n\nIf the training error is still something huge like `0.4` after 20000 iterations, it's a good sign that the network can't make sense of the data you're giving it.\n\n## JSON\nSerialize or load in the state of a trained network with JSON:\n\n```javascript\nvar json = net.toJSON();\n\nnet.fromJSON(json);\n```\n\nYou can also get a custom standalone function from a trained network that acts just like `run()`:\n\n```javascript\nvar run = net.toFunction();\n\nvar output = run({ r: 1, g: 0.4, b: 0 });\n\nconsole.log(run.toString()); // copy and paste! no need to import brain.js\n```\n\n## Options\n`NeuralNetwork()` takes a hash of options:\n\n```javascript\nvar net = new brain.NeuralNetwork({\n  hiddenLayers: [4],\n  learningRate: 0.6 // global learning rate, useful when training using streams\n});\n```\n\n#### hiddenLayers\nSpecify the number of hidden layers in the network and the size of each layer. For example, if you want two hidden layers - the first with 3 nodes and the second with 4 nodes, you'd give:\n\n```\nhiddenLayers: [3, 4]\n```\n\nBy default `brain` uses one hidden layer with size proportionate to the size of the input array.\n\n## Streams\nThe network now has a [WriteStream](http://nodejs.org/api/stream.html#stream_class_stream_writable). You can train the network by using `pipe()` to send the training data to the network.\n\n#### Example\nRefer to `stream-example.js` for an example on how to train the network with a stream.\n\n#### Initialization\nTo train the network using a stream you must first create the stream by calling `net.createTrainStream()` which takes the following options:\n\n* `floodCallback()` - the callback function to re-populate the stream. This gets called on every training iteration.\n* `doneTrainingCallback(info)` - the callback function to execute when the network is done training. The `info` param will contain a hash of information about how the training went:\n\n```javascript\n{\n  error: 0.0039139985510105032,  // training error\n  iterations: 406                // training iterations\n}\n```\n\n#### Transform\nUse a [Transform](http://nodejs.org/api/stream.html#stream_class_stream_transform) to coerce the data into the correct format. You might also use a Transform stream to normalize your data on the fly.\n"
  },
  {
    "path": "bower.json",
    "content": "{\n  \"name\": \"brain\",\n  \"version\": \"0.7.0\",\n  \"homepage\": \"https://github.com/harthur/brain\",\n  \"authors\": [\n    \"Heather Arthur <fayearthur@gmail.com>\"\n  ],\n  \"description\": \"Neural network library\",\n  \"keywords\": [\n    \"neural-networks\",\n    \"machine-learning\",\n    \"classifier\"\n  ],\n  \"main\": \"lib/brain.js\",\n  \"ignore\": [\n    \"node_modules\",\n    \"test\"\n  ],\n  \"dependencies\": {\n    \"underscore\": \">=1.5.1\"\n  }\n}\n"
  },
  {
    "path": "browser.js",
    "content": "// this file is the entrypoint for building a browser file with browserify\n\nbrain = require(\"./lib/brain\");"
  },
  {
    "path": "lib/brain.js",
    "content": "exports.NeuralNetwork = require(\"./neuralnetwork\").NeuralNetwork;\nexports.crossValidate = require(\"./cross-validate\");\n"
  },
  {
    "path": "lib/cross-validate.js",
    "content": "var _ = require(\"underscore\")._;\n\nfunction testPartition(classifierConst, opts, trainOpts, trainSet, testSet) {\n  var classifier = new classifierConst(opts);\n\n  var beginTrain = Date.now();\n\n  var trainingStats = classifier.train(trainSet, trainOpts);\n\n  var beginTest = Date.now();\n\n  var testStats = classifier.test(testSet);\n\n  var endTest = Date.now();\n\n  var stats = _(testStats).extend({\n    trainTime : beginTest - beginTrain,\n    testTime : endTest - beginTest,\n    iterations: trainingStats.iterations,\n    trainError: trainingStats.error,\n    learningRate: trainOpts.learningRate,\n    hidden: classifier.hiddenSizes,\n    network: classifier.toJSON()\n  });\n\n  return stats;\n}\n\nmodule.exports = function crossValidate(classifierConst, data, opts, trainOpts, k) {\n  k = k || 4;\n  var size = data.length / k;\n\n  data = _(data).sortBy(function() {\n    return Math.random();\n  });\n\n  var avgs = {\n    error : 0,\n    trainTime : 0,\n    testTime : 0,\n    iterations: 0,\n    trainError: 0\n  };\n\n  var stats = {\n    truePos: 0,\n    trueNeg: 0,\n    falsePos: 0,\n    falseNeg: 0,\n    total: 0\n  };\n\n  var misclasses = [];\n\n  var results = _.range(k).map(function(i) {\n    var dclone = _(data).clone();\n    var testSet = dclone.splice(i * size, size);\n    var trainSet = dclone;\n\n    var result = testPartition(classifierConst, opts, trainOpts, trainSet, testSet);\n\n    _(avgs).each(function(sum, stat) {\n      avgs[stat] = sum + result[stat];\n    });\n\n    _(stats).each(function(sum, stat) {\n      stats[stat] = sum + result[stat];\n    })\n\n    misclasses.push(result.misclasses);\n\n    return result;\n  });\n\n  _(avgs).each(function(sum, i) {\n    avgs[i] = sum / k;\n  });\n\n  stats.precision = stats.truePos / (stats.truePos + stats.falsePos);\n  stats.recall = stats.truePos / (stats.truePos + stats.falseNeg);\n  stats.accuracy = (stats.trueNeg + stats.truePos) / stats.total;\n\n  stats.testSize = size;\n  stats.trainSize = data.length - size;\n\n  return {\n    avgs: avgs,\n    stats: stats,\n    sets: results,\n    misclasses: _(misclasses).flatten()\n  };\n}\n"
  },
  {
    "path": "lib/lookup.js",
    "content": "var _ = require(\"underscore\");\n\n/* Functions for turning sparse hashes into arrays and vice versa */\n\nfunction buildLookup(hashes) {\n  // [{a: 1}, {b: 6, c: 7}] -> {a: 0, b: 1, c: 2}\n  var hash = _(hashes).reduce(function(memo, hash) {\n    return _(memo).extend(hash);\n  }, {});\n  return lookupFromHash(hash);\n}\n\nfunction lookupFromHash(hash) {\n  // {a: 6, b: 7} -> {a: 0, b: 1}\n  var lookup = {};\n  var index = 0;\n  for (var i in hash) {\n    lookup[i] = index++;\n  }\n  return lookup;\n}\n\nfunction toArray(lookup, hash) {\n  // {a: 0, b: 1}, {a: 6} -> [6, 0]\n  var array = [];\n  for (var i in lookup) {\n    array[lookup[i]] = hash[i] || 0;\n  }\n  return array;\n}\n\nfunction toHash(lookup, array) {\n  // {a: 0, b: 1}, [6, 7] -> {a: 6, b: 7}\n  var hash = {};\n  for (var i in lookup) {\n    hash[i] = array[lookup[i]];\n  }\n  return hash;\n}\n\nfunction lookupFromArray(array) {\n  var lookup = {};\n  // super fast loop\n  var z = 0;\n  var i = array.length;\n  while (i-- > 0) {\n    lookup[array[i]] = z++;\n  };\n  return lookup;\n}\n\nmodule.exports = {\n  buildLookup: buildLookup,\n  lookupFromHash: lookupFromHash,\n  toArray: toArray,\n  toHash: toHash,\n  lookupFromArray: lookupFromArray\n};"
  },
  {
    "path": "lib/neuralnetwork.js",
    "content": "var _ = require(\"underscore\"),\n    lookup = require(\"./lookup\"),\n    Writable = require('stream').Writable,\n    inherits = require('inherits');\n\nvar NeuralNetwork = function(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\nNeuralNetwork.prototype = {\n  initialize: function(sizes) {\n    this.sizes = sizes;\n    this.outputLayer = this.sizes.length - 1;\n\n    this.biases = []; // weights for bias nodes\n    this.weights = [];\n    this.outputs = [];\n\n    // state for training\n    this.deltas = [];\n    this.changes = []; // for momentum\n    this.errors = [];\n\n    for (var layer = 0; layer <= this.outputLayer; layer++) {\n      var size = this.sizes[layer];\n      this.deltas[layer] = zeros(size);\n      this.errors[layer] = zeros(size);\n      this.outputs[layer] = zeros(size);\n\n      if (layer > 0) {\n        this.biases[layer] = randos(size);\n        this.weights[layer] = new Array(size);\n        this.changes[layer] = new Array(size);\n\n        for (var node = 0; node < size; node++) {\n          var prevSize = this.sizes[layer - 1];\n          this.weights[layer][node] = randos(prevSize);\n          this.changes[layer][node] = zeros(prevSize);\n        }\n      }\n    }\n  },\n\n  run: function(input) {\n    if (this.inputLookup) {\n      input = lookup.toArray(this.inputLookup, input);\n    }\n\n    var output = this.runInput(input);\n\n    if (this.outputLookup) {\n      output = lookup.toHash(this.outputLookup, output);\n    }\n    return output;\n  },\n\n  runInput: function(input) {\n    this.outputs[0] = input;  // set output state of input layer\n\n    for (var layer = 1; layer <= this.outputLayer; layer++) {\n      for (var node = 0; node < this.sizes[layer]; node++) {\n        var weights = this.weights[layer][node];\n\n        var sum = this.biases[layer][node];\n        for (var 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      var output = input = this.outputs[layer];\n    }\n    return output;\n  },\n\n  train: function(data, options) {\n    data = this.formatData(data);\n\n    options = options || {};\n    var iterations = options.iterations || 20000;\n    var errorThresh = options.errorThresh || 0.005;\n    var log = options.log ? (_.isFunction(options.log) ? options.log : console.log) : false;\n    var logPeriod = options.logPeriod || 10;\n    var learningRate = options.learningRate || this.learningRate || 0.3;\n    var callback = options.callback;\n    var callbackPeriod = options.callbackPeriod || 10;\n\n    var inputSize = data[0].input.length;\n    var outputSize = data[0].output.length;\n\n    var hiddenSizes = this.hiddenSizes;\n    if (!hiddenSizes) {\n      hiddenSizes = [Math.max(3, Math.floor(inputSize / 2))];\n    }\n    var sizes = _([inputSize, hiddenSizes, outputSize]).flatten();\n    this.initialize(sizes);\n\n    var error = 1;\n    for (var i = 0; i < iterations && error > errorThresh; i++) {\n      var sum = 0;\n      for (var j = 0; j < data.length; j++) {\n        var 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  trainPattern : function(input, target, learningRate) {\n    learningRate = learningRate || this.learningRate;\n\n    // forward propogate\n    this.runInput(input);\n\n    // back propogate\n    this.calculateDeltas(target);\n    this.adjustWeights(learningRate);\n\n    var error = mse(this.errors[this.outputLayer]);\n    return error;\n  },\n\n  calculateDeltas: function(target) {\n    for (var layer = this.outputLayer; layer >= 0; layer--) {\n      for (var node = 0; node < this.sizes[layer]; node++) {\n        var output = this.outputs[layer][node];\n\n        var error = 0;\n        if (layer == this.outputLayer) {\n          error = target[node] - output;\n        }\n        else {\n          var deltas = this.deltas[layer + 1];\n          for (var 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  adjustWeights: function(learningRate) {\n    for (var layer = 1; layer <= this.outputLayer; layer++) {\n      var incoming = this.outputs[layer - 1];\n\n      for (var node = 0; node < this.sizes[layer]; node++) {\n        var delta = this.deltas[layer][node];\n\n        for (var k = 0; k < incoming.length; k++) {\n          var 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  formatData: function(data) {\n    if (!_.isArray(data)) { // turn stream datum into array\n      var tmp = [];\n      tmp.push(data);\n      data = tmp;\n    }\n    // turn sparse hash input into arrays with 0s as filler\n    var datum = data[0].input;\n    if (!_(datum).isArray() && !(datum instanceof Float64Array)) {\n      if (!this.inputLookup) {\n        this.inputLookup = lookup.buildLookup(_(data).pluck(\"input\"));\n      }\n      data = data.map(function(datum) {\n        var array = lookup.toArray(this.inputLookup, datum.input)\n        return _(_(datum).clone()).extend({ input: array });\n      }, this);\n    }\n\n    if (!_(data[0].output).isArray()) {\n      if (!this.outputLookup) {\n        this.outputLookup = lookup.buildLookup(_(data).pluck(\"output\"));\n      }\n      data = data.map(function(datum) {\n        var array = lookup.toArray(this.outputLookup, datum.output);\n        return _(_(datum).clone()).extend({ output: array });\n      }, this);\n    }\n    return data;\n  },\n\n  test : function(data) {\n    data = this.formatData(data);\n\n    // for binary classification problems with one output node\n    var isBinary = data[0].output.length == 1;\n    var falsePos = 0,\n        falseNeg = 0,\n        truePos = 0,\n        trueNeg = 0;\n\n    // for classification problems\n    var misclasses = [];\n\n    // run each pattern through the trained network and collect\n    // error and misclassification statistics\n    var sum = 0;\n    for (var i = 0; i < data.length; i++) {\n      var output = this.runInput(data[i].input);\n      var target = data[i].output;\n\n      var actual, expected;\n      if (isBinary) {\n        actual = output[0] > this.binaryThresh ? 1 : 0;\n        expected = target[0];\n      }\n      else {\n        actual = output.indexOf(_(output).max());\n        expected = target.indexOf(_(target).max());\n      }\n\n      if (actual != expected) {\n        var misclass = data[i];\n        _(misclass).extend({\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      var errors = output.map(function(value, i) {\n        return target[i] - value;\n      });\n      sum += mse(errors);\n    }\n    var error = sum / data.length;\n\n    var stats = {\n      error: error,\n      misclasses: misclasses\n    };\n\n    if (isBinary) {\n      _(stats).extend({\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  toJSON: function() {\n    /* make json look like:\n      {\n        layers: [\n          { x: {},\n            y: {}},\n          {'0': {bias: -0.98771313, weights: {x: 0.8374838, y: 1.245858},\n           '1': {bias: 3.48192004, weights: {x: 1.7825821, y: -2.67899}}},\n          { f: {bias: 0.27205739, weights: {'0': 1.3161821, '1': 2.00436}}}\n        ]\n      }\n    */\n    var layers = [];\n    for (var layer = 0; layer <= this.outputLayer; layer++) {\n      layers[layer] = {};\n\n      var nodes;\n      // turn any internal arrays back into hashes for readable json\n      if (layer == 0 && this.inputLookup) {\n        nodes = _(this.inputLookup).keys();\n      }\n      else if (layer == this.outputLayer && this.outputLookup) {\n        nodes = _(this.outputLookup).keys();\n      }\n      else {\n        nodes = _.range(0, this.sizes[layer]);\n      }\n\n      for (var j = 0; j < nodes.length; j++) {\n        var 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 (var k in layers[layer - 1]) {\n            var 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  fromJSON: function(json) {\n    var 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 (var i = 0; i <= this.outputLayer; i++) {\n      var 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      var nodes = _(layer).keys();\n      this.sizes[i] = nodes.length;\n      this.weights[i] = [];\n      this.biases[i] = [];\n      this.outputs[i] = [];\n\n      for (var j in nodes) {\n        var node = nodes[j];\n        this.biases[i][j] = layer[node].bias;\n        this.weights[i][j] = _(layer[node].weights).toArray();\n      }\n    }\n    return this;\n  },\n\n   toFunction: function() {\n    var json = this.toJSON();\n    // return standalone function that mimics run()\n    return new Function(\"input\",\n'  var net = ' + JSON.stringify(json) + ';\\n\\n\\\n  for (var i = 1; i < net.layers.length; i++) {\\n\\\n    var layer = net.layers[i];\\n\\\n    var output = {};\\n\\\n    \\n\\\n    for (var id in layer) {\\n\\\n      var node = layer[id];\\n\\\n      var sum = node.bias;\\n\\\n      \\n\\\n      for (var iid in node.weights) {\\n\\\n        sum += node.weights[iid] * input[iid];\\n\\\n      }\\n\\\n      output[id] = (1 / (1 + Math.exp(-sum)));\\n\\\n    }\\n\\\n    input = output;\\n\\\n  }\\n\\\n  return output;');\n  },\n\n  // This will create a TrainStream (WriteStream)\n  //  for us to send the training data to.\n  //  param: opts - the training options\n  createTrainStream: function(opts) {\n    opts = opts || {};\n    opts.neuralNetwork = this;\n    this.trainStream = new TrainStream(opts);\n    return this.trainStream;\n  }\n}\n\nfunction randomWeight() {\n  return Math.random() * 0.4 - 0.2;\n}\n\nfunction zeros(size) {\n  var array = new Array(size);\n  for (var i = 0; i < size; i++) {\n    array[i] = 0;\n  }\n  return array;\n}\n\nfunction randos(size) {\n  var array = new Array(size);\n  for (var i = 0; i < size; i++) {\n    array[i] = randomWeight();\n  }\n  return array;\n}\n\nfunction mse(errors) {\n  // mean squared error\n  var sum = 0;\n  for (var i = 0; i < errors.length; i++) {\n    sum += Math.pow(errors[i], 2);\n  }\n  return sum / errors.length;\n}\n\nexports.NeuralNetwork = NeuralNetwork;\n\nfunction TrainStream(opts) {\n  Writable.call(this, {\n    objectMode: true\n  });\n\n  opts = opts || {};\n\n  // require the neuralNetwork\n  if (!opts.neuralNetwork) {\n    throw new Error('no neural network specified');\n  }\n\n  this.neuralNetwork = opts.neuralNetwork;\n  this.dataFormatDetermined = false;\n\n  this.inputKeys = [];\n  this.outputKeys = []; // keeps track of keys seen\n  this.i = 0; // keep track of the for loop i variable that we got rid of\n  this.iterations = opts.iterations || 20000;\n  this.errorThresh = opts.errorThresh || 0.005;\n  this.log = opts.log ? (_.isFunction(opts.log) ? opts.log : console.log) : false;\n  this.logPeriod = opts.logPeriod || 10;\n  this.callback = opts.callback;\n  this.callbackPeriod = opts.callbackPeriod || 10;\n  this.floodCallback = opts.floodCallback;\n  this.doneTrainingCallback = opts.doneTrainingCallback;\n\n  this.size = 0;\n  this.count = 0;\n\n  this.sum = 0;\n\n  this.on('finish', this.finishStreamIteration);\n\n  return this;\n}\n\ninherits(TrainStream, Writable);\n\n/*\n  _write expects data to be in the form of a datum.\n  ie. {input: {a: 1 b: 0}, output: {z: 0}}\n */\nTrainStream.prototype._write = function(chunk, enc, next) {\n  if (!chunk) { // check for the end of one interation of the stream\n    this.emit('finish');\n    return next();\n  }\n\n  if (!this.dataFormatDetermined) {\n    this.size++;\n    this.inputKeys = _.union(this.inputKeys, _.keys(chunk.input));\n    this.outputKeys = _.union(this.outputKeys, _.keys(chunk.output));\n    this.firstDatum = this.firstDatum || chunk;\n    return next();\n  }\n\n  this.count++;\n\n  var data = this.neuralNetwork.formatData(chunk);\n  this.trainDatum(data[0]);\n\n  // tell the Readable Stream that we are ready for more data\n  next();\n}\n\nTrainStream.prototype.trainDatum = function(datum) {\n  var err = this.neuralNetwork.trainPattern(datum.input, datum.output);\n  this.sum += err;\n}\n\nTrainStream.prototype.finishStreamIteration = function() {\n  if (this.dataFormatDetermined && this.size !== this.count) {\n    console.log(\"This iteration's data length was different from the first.\");\n  }\n\n  if (!this.dataFormatDetermined) {\n    // create the lookup\n    this.neuralNetwork.inputLookup = lookup.lookupFromArray(this.inputKeys);\n    if(!_.isArray(this.firstDatum.output)){\n      this.neuralNetwork.outputLookup = lookup.lookupFromArray(this.outputKeys);\n    }\n\n    var data = this.neuralNetwork.formatData(this.firstDatum);\n    var inputSize = data[0].input.length;\n    var outputSize = data[0].output.length;\n\n    var hiddenSizes = this.hiddenSizes;\n    if (!hiddenSizes) {\n      hiddenSizes = [Math.max(3, Math.floor(inputSize / 2))];\n    }\n    var sizes = _([inputSize, hiddenSizes, outputSize]).flatten();\n    this.dataFormatDetermined = true;\n    this.neuralNetwork.initialize(sizes);\n\n    if (typeof this.floodCallback === 'function') {\n      this.floodCallback();\n    }\n    return;\n  }\n\n  var error = this.sum / this.size;\n\n  if (this.log && (this.i % this.logPeriod == 0)) {\n    this.log(\"iterations:\", this.i, \"training error:\", error);\n  }\n  if (this.callback && (this.i % this.callbackPeriod == 0)) {\n    this.callback({\n      error: error,\n      iterations: this.i\n    });\n  }\n\n  this.sum = 0;\n  this.count = 0;\n  // update the iterations\n  this.i++;\n\n  // do a check here to see if we need the stream again\n  if (this.i < this.iterations && error > this.errorThresh) {\n    if (typeof this.floodCallback === 'function') {\n      return this.floodCallback();\n    }\n  } else {\n    // done training\n    if (typeof this.doneTrainingCallback === 'function') {\n      return this.doneTrainingCallback({\n        error: error,\n        iterations: this.i\n      });\n    }\n  }\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n    \"name\": \"brain\",\n    \"description\": \"Neural network library\",\n    \"version\": \"0.7.0\",\n    \"author\": \"Heather Arthur <fayearthur@gmail.com>\",\n    \"repository\": {\n        \"type\": \"git\",\n        \"url\": \"http://github.com/harthur/brain.git\"\n    },\n    \"scripts\": {\n        \"test-unit\": \"mocha test/unit\",\n        \"test-cv\": \"mocha test/cross-validation --timeout 10000\",\n        \"test\": \"npm run test-unit && npm run test-cv\"\n    },\n    \"main\": \"./lib/brain\",\n    \"dependencies\": {\n        \"underscore\": \">=1.5.1\",\n        \"inherits\": \"~2.0.1\"\n    },\n    \"devDependencies\": {\n        \"mocha\": \">=1.0.0\",\n        \"canvas\": \">=0.10.0\",\n        \"grunt\": \"~0.4.3\",\n        \"grunt-contrib-uglify\": \"~0.2.0\",\n        \"grunt-mocha-test\": \"~0.11.0\",\n        \"browserify\": \"~3.32.0\"\n    },\n    \"keywords\": [\"neural network\", \"classifier\", \"machine learning\"]\n}\n"
  },
  {
    "path": "stream-example.js",
    "content": "var assert = require(\"assert\"),\n    brain = require(\"./lib/brain\");\n\nvar net = new brain.NeuralNetwork();\n\nvar xor = [\n  { input: [0, 0], output: [0]},\n  { input: [0, 1], output: [1]},\n  { input: [1, 0], output: [1]},\n  { input: [1, 1], output: [0]}];\n\nvar trainStream = net.createTrainStream({\n  /**\n   * Write training data to the stream. Called on each training iteration.\n   */\n  floodCallback: function() {\n    flood(trainStream, xor);\n  },\n\n  /**\n   * Called when the network is done training.\n   */\n  doneTrainingCallback: function(obj) {\n    console.log(\"trained in \" + obj.iterations + \" iterations with error: \"\n                + obj.error);\n\n    var result = net.run([0, 1]);\n\n    console.log(\"0 XOR 1: \", result);  // 0.987\n  }\n});\n\n// kick it off\nflood(trainStream, xor);\n\n\nfunction flood(stream, data) {\n  for (var i = 0; i < data.length; i++) {\n    stream.write(data[i]);\n  }\n  // let it know we've reached the end of the data\n  stream.write(null);\n}\n"
  },
  {
    "path": "test/README.md",
    "content": "# Tests\n\nTo run the tests in this directory, make sure you've installed the dev dependencies with this command from the top-level directory:\n\n```\nnpm install\n```\n\nThen you can run all tests (unit and cross-validation) using `npm test`.\n\n# Unit tests\nRun the unit tests with:\n\n```\ngrunt test\n```\n\nor\n\n`npm run test-unit`\n\n# Cross-validation tests\nThe cross-validation tests will actually test how good the neural network is a training by getting a bunch of training data, training it with some, and using the rest as verification.\n\nCross-validation tests will take a long time to run, and in the end will give you a printout with the average error of the test data.\n\nRun these with:\n\n```\nmocha test/cross-validation/* --timeout 10000\n```\n\nor\n\n`npm run test-cv`\n"
  },
  {
    "path": "test/cross-validation/ocr.js",
    "content": "var canvas = require(\"canvas\"),\n    _ = require(\"underscore\"),\n    brain = require(\"../../lib/brain\"),\n    crossValidate = require(\"../../lib/cross-validate\");\n\nvar dim = 24;\n\nfunction getSampling(context, letter, font) {\n  context.clearRect(0, 0, dim, dim);\n  context.font = dim + \"px \" + font;\n  context.fillText(letter, 0, dim);\n\n  var data = context.getImageData(0, 0, dim, dim);\n  var lumas = extractPoints(data);\n  return lumas;\n}\n\nfunction extractPoints(imageData) {\n  var points = [];\n  for (var x = 0; x < imageData.width; x = x + 2) {\n    for (var y = 0; y < imageData.height; y = y + 2) {\n      var i = x * 4 + y * 4 * imageData.width;\n      var r = imageData.data[i],\n          g = imageData.data[i + 1],\n          b = imageData.data[i + 2],\n          a = imageData.data[i + 3];\n\n      var luma = a == 0 ? 1 : (r * 299/1000 + g * 587/1000\n        + b * 114/1000 ) / 255;\n\n      points.push(luma);\n    }\n  }\n  return points;\n}\n\ndescribe('OCR cross-validation', function() {\n  it('recognize characters in different fonts', function() {\n    var canvas = require(\"canvas\");\n    var canvas = new canvas(dim, dim);\n    var context = canvas.getContext(\"2d\");\n\n    var letters = [\"A\", \"B\", \"C\", \"D\", \"E\",\n                   \"K\", \"O\", \"Z\"];\n    var fonts = [\"Arial\", \"Courier\", \"Georgia\", \"Menlo\", \"Optima\",\n                 \"Copperplate\", \"American Typewriter\", \"Comic Sans\",\n                 \"Baskerville\", \"Verdana\", \"Helvetica\", \"Didot\",\n                 \"Geneva\", \"Cracked\", \"Impact\", \"Cooper\"];\n\n    var data = [];\n\n    letters.forEach(function(letter) {\n       fonts.forEach(function(font) {\n          var input = getSampling(context, letter, font);\n\n          var output = {};\n          output[letter] = 1;\n          data.push({ input: input, output: output });\n       });\n    });\n\n    console.log(\"Cross validating\");\n    var result = crossValidate(brain.NeuralNetwork, data, {});\n\n    console.log(\"\\nMisclassifications:\");\n    result.misclasses.forEach(function(misclass) {\n      console.log(\"input: \" + misclass.input\n        + \" actual: \" + letters[misclass.actual]\n        + \" expected: \" + letters[misclass.expected] + \"\\n\")\n    })\n\n    console.log(\"\\nCross-validation of OCR data:\\n\");\n    console.log(result.avgs);\n\n    console.log(\"\\nMisclassification rate: \"\n      + result.misclasses.length / data.length);\n\n    console.log(\"\\nMean squared error: \"\n      + result.avgs.error);\n\n    var perf = result.avgs.iterations / (result.avgs.trainTime / 1000);\n    console.log(\"\\nTraining iterations per second: \" + perf);\n\n    assert.ok(result.avgs.error < .1);\n  })\n})\n"
  },
  {
    "path": "test/unit/bitwise.js",
    "content": "var assert = require(\"assert\"),\n    brain = require(\"../../lib/brain\");\n\nvar wiggle = 0.1;\n\nfunction testBitwise(data, op) {\n  var net = new brain.NeuralNetwork();\n  net.train(data, { errorThresh: 0.003 });\n\n  for(var i in data) {\n    var output = net.run(data[i].input);\n    var target = data[i].output;\n    assert.ok(output < (target + wiggle) && output > (target - wiggle),\n     \"failed to train \" + op + \" - output: \" + output + \" target: \" + target);\n  }\n}\n\ndescribe('bitwise functions', function() {\n\n  it('NOT function', function() {\n    var not = [{input: [0], output: [1]},\n               {input: [1], output: [0]}];\n    testBitwise(not, \"not\");\n  })\n\n  it('XOR function', function() {\n    var xor = [{input: [0, 0], output: [0]},\n               {input: [0, 1], output: [1]},\n               {input: [1, 0], output: [1]},\n               {input: [1, 1], output: [0]}];\n    testBitwise(xor, \"xor\");\n  })\n\n  it('OR function', function() {\n    var or = [{input: [0, 0], output: [0]},\n              {input: [0, 1], output: [1]},\n              {input: [1, 0], output: [1]},\n              {input: [1, 1], output: [1]}];\n    testBitwise(or, \"or\");\n  });\n\n  it('AND function', function() {\n    var and = [{input: [0, 0], output: [0]},\n               {input: [0, 1], output: [0]},\n               {input: [1, 0], output: [0]},\n               {input: [1, 1], output: [1]}];\n    testBitwise(and, \"and\");\n  })\n})\n"
  },
  {
    "path": "test/unit/hash.js",
    "content": "var assert = require(\"assert\"),\n    brain = require(\"../../lib/brain\");\n\ndescribe('hash input and output', function() {\n  it('runs correctly with array input and output', function() {\n    var net = new brain.NeuralNetwork();\n\n    net.train([{input: [0, 0], output: [0]},\n               {input: [0, 1], output: [1]},\n               {input: [1, 0], output: [1]},\n               {input: [1, 1], output: [0]}]);\n    var output = net.run([1, 0]);\n\n    assert.ok(output[0] > 0.9, \"output: \" + output[0]);\n  })\n\n it('runs correctly with hash input', function() {\n    var net = new brain.NeuralNetwork();\n\n    var info = net.train([{input: { x: 0, y: 0 }, output: [0]},\n               {input: { x: 0, y: 1 }, output: [1]},\n               {input: { x: 1, y: 0 }, output: [1]},\n               {input: { x: 1, y: 1 }, output: [0]}]);\n    var output = net.run({x: 1, y: 0});\n\n    assert.ok(output[0] > 0.9, \"output: \" + output[0]);\n  })\n\n it('runs correctly with hash output', function() {\n    var net = new brain.NeuralNetwork();\n\n    net.train([{input: [0, 0], output: { answer: 0 }},\n               {input: [0, 1], output: { answer: 1 }},\n               {input: [1, 0], output: { answer: 1 }},\n               {input: [1, 1], output: { answer: 0 }}]);\n\n    var output = net.run([1, 0]);\n\n    assert.ok(output.answer > 0.9, \"output: \" + output.answer);\n  })\n\n  it('runs correctly with hash input and output', function() {\n    var net = new brain.NeuralNetwork();\n\n    net.train([{input: { x: 0, y: 0 }, output: { answer: 0 }},\n               {input: { x: 0, y: 1 }, output: { answer: 1 }},\n               {input: { x: 1, y: 0 }, output: { answer: 1 }},\n               {input: { x: 1, y: 1 }, output: { answer: 0 }}]);\n\n    var output = net.run({x: 1, y: 0});\n\n    assert.ok(output.answer > 0.9, \"output: \" + output.answer);\n  })\n\n  it('runs correctly with sparse hashes', function() {\n      var net = new brain.NeuralNetwork();\n\n      net.train([{input: {}, output: {}},\n                 {input: { y: 1 }, output: { answer: 1 }},\n                 {input: { x: 1 }, output: { answer: 1 }},\n                 {input: { x: 1, y: 1 }, output: {}}]);\n\n\n      var output = net.run({x: 1});\n\n      assert.ok(output.answer > 0.9);\n  })\n\n  it('runs correctly with unseen input', function() {\n      var net = new brain.NeuralNetwork();\n\n      net.train([{input: {}, output: {}},\n                 {input: { y: 1 }, output: { answer: 1 }},\n                 {input: { x: 1 }, output: { answer: 1 }},\n                 {input: { x: 1, y: 1 }, output: {}}]);\n\n      var output = net.run({x: 1, z: 1});\n      assert.ok(output.answer > 0.9);\n  })\n})\n"
  },
  {
    "path": "test/unit/json.js",
    "content": "var assert = require(\"assert\"),\n    brain = require(\"../../lib/brain\");\n\ndescribe('JSON', function() {\n  var net = new brain.NeuralNetwork();\n\n  net.train([{input:  {\"0\": Math.random(), b: Math.random()},\n              output: {c: Math.random(), \"0\": Math.random()}},\n             {input:  {\"0\": Math.random(), b: Math.random()},\n              output: {c: Math.random(), \"0\": Math.random()}}]);\n\n  var serialized = net.toJSON();\n  var net2 = new brain.NeuralNetwork().fromJSON(serialized);\n\n  var input = {\"0\" : Math.random(), b: Math.random()};\n\n  it('toJSON()/fromJSON()', function() {\n    var output1 = net.run(input);\n    var output2 = net2.run(input);\n\n    assert.equal(JSON.stringify(output1), JSON.stringify(output2),\n                  \"loading json serialized network failed\");\n  })\n\n\n  it('toFunction()', function() {\n    var output1 = net.run(input);\n    var output2 = net.toFunction()(input);\n\n    assert.equal(JSON.stringify(output1), JSON.stringify(output2),\n                   \"standalone network function failed\");\n  })\n})\n"
  },
  {
    "path": "test/unit/lookup.js",
    "content": "var assert = require(\"assert\"),\n    lookup = require(\"../../lib/lookup\");\n\n\ndescribe('lookup', function() {\n  it('lookupFromHash()', function() {\n    var lup = lookup.lookupFromHash({ a: 6, b: 7, c: 8 });\n\n    assert.deepEqual(lup, { a: 0, b: 1, c: 2 });\n  })\n\n  it('buildLookup()', function() {\n    var lup = lookup.buildLookup([{ x: 0, y: 0 },\n      { x: 1, z: 0 },\n      { q: 0 },\n      { x: 1, y: 1 }]);\n\n    assert.deepEqual(lup, { x: 0, y: 1, z: 2, q: 3 })\n  })\n\n  it('toArray()', function() {\n    var lup = { a: 0, b: 1, c: 2 };\n\n    var array = lookup.toArray(lup, { b: 8, notinlookup: 9 });\n\n    assert.deepEqual(array, [0, 8, 0])\n  })\n\n  it('toHash()', function() {\n    var lup = { b: 1, a: 0, c: 2 };\n\n    var hash = lookup.toHash(lup, [0, 9, 8]);\n\n    assert.deepEqual(hash, {a: 0, b: 9, c: 8})\n  })\n})\n"
  },
  {
    "path": "test/unit/options.js",
    "content": "var assert = require(\"assert\"),\n    _ = require(\"underscore\"),\n    brain = require(\"../../lib/brain\");\n\ndescribe('neural network options', function() {\n  it('hiddenLayers', function() {\n    var net = new brain.NeuralNetwork({ hiddenLayers: [8, 7] });\n\n    net.train([{input: [0, 0], output: [0]},\n               {input: [0, 1], output: [1]},\n               {input: [1, 0], output: [1]},\n               {input: [1, 1], output: [0]}]);\n\n    var json = net.toJSON();\n\n    assert.equal(json.layers.length, 4);\n    assert.equal(_(json.layers[1]).keys().length, 8);\n    assert.equal(_(json.layers[2]).keys().length, 7);\n  })\n\n  it('hiddenLayers default expand to input size', function() {\n    var net = new brain.NeuralNetwork();\n\n    net.train([{input: [0, 0, 1, 1, 1, 1, 1, 1, 1], output: [0]},\n               {input: [0, 1, 1, 1, 1, 1, 1, 1, 1], output: [1]},\n               {input: [1, 0, 1, 1, 1, 1, 1, 1, 1], output: [1]},\n               {input: [1, 1, 1, 1, 1, 1, 1, 1, 1], output: [0]}]);\n\n    var json = net.toJSON();\n\n    assert.equal(json.layers.length, 3);\n    assert.equal(_(json.layers[1]).keys().length, 4, \"9 input units means 4 hidden\");\n  })\n\n\n  it('learningRate - higher learning rate should train faster', function() {\n    var data = [{input: [0, 0], output: [0]},\n                {input: [0, 1], output: [1]},\n                {input: [1, 0], output: [1]},\n                {input: [1, 1], output: [1]}];\n\n    var net1 = new brain.NeuralNetwork();\n    var iters1 = net1.train(data, { learningRate: 0.5 }).iterations;\n\n    var net2 = new brain.NeuralNetwork();\n    var iters2 = net2.train(data, { learningRate: 0.8 }).iterations;\n\n    assert.ok(iters1 > (iters2 * 1.1), iters1 + \" !> \" + iters2 * 1.1);\n  })\n\n  it('learningRate - backwards compatibility', function() {\n    var data = [{input: [0, 0], output: [0]},\n                {input: [0, 1], output: [1]},\n                {input: [1, 0], output: [1]},\n                {input: [1, 1], output: [1]}];\n\n    var net1 = new brain.NeuralNetwork({ learningRate: 0.5 });\n    var iters1 = net1.train(data).iterations;\n\n    var net2 = new brain.NeuralNetwork( { learningRate: 0.8 });\n    var iters2 = net2.train(data).iterations;\n\n    assert.ok(iters1 > (iters2 * 1.1), iters1 + \" !> \" + iters2 * 1.1);\n  })\n\n  it('momentum - higher momentum should train faster', function() {\n    var data = [{input: [0, 0], output: [0]},\n                {input: [0, 1], output: [1]},\n                {input: [1, 0], output: [1]},\n                {input: [1, 1], output: [1]}];\n\n    var net1 = new brain.NeuralNetwork({ momentum: 0.1 });\n    var iters1 = net1.train(data).iterations;\n\n    var net2 = new brain.NeuralNetwork({ momentum: 0.5 });\n    var iters2 = net2.train(data).iterations;\n\n    assert.ok(iters1 > (iters2 * 1.1), iters1 + \" !> \" + (iters2 * 1.1));\n  })\n\n  describe('log', function () {\n    var logCalled;\n\n    beforeEach(function () {\n      logCalled = false;\n    });\n\n    function logFunction() {\n      logCalled = true;\n    }\n\n    function trainWithLog(log) {\n      var net = new brain.NeuralNetwork();\n      net.train([{input: [0], output: [0]}],\n        {\n          log: log,\n          logPeriod: 1\n        });\n      }\n\n      it('should call console.log if log === true', function () {\n        var originalLog = console.log;\n        console.log = logFunction;\n\n        trainWithLog(true);\n\n        console.log = originalLog;\n        assert.equal(logCalled, true);\n      })\n\n      it('should call the given log function', function () {\n        trainWithLog(logFunction);\n\n        assert.equal(logCalled, true);\n      })\n  })\n})\n"
  },
  {
    "path": "test/unit/stream-bitwise.js",
    "content": "var assert = require(\"assert\"),\n    brain = require(\"../../lib/brain\");\n\nfunction StreamTester(opts) {\n  if (!(this instanceof StreamTester)) return new StreamTester(opts);\n\n  var self = this;\n\n  this.wiggle = opts.wiggle || 0.1;\n  this.op = opts.op;\n\n  this.testData = opts.testData;\n  this.fakeBuffer = [];\n  this.errorThresh = opts.errorThresh || 0.004;\n\n  this.net = new brain.NeuralNetwork();\n\n  this.trainStream = this.net.createTrainStream({\n    floodCallback: self.flood.bind(self),\n    doneTrainingCallback: self.doneTraining.bind(self),\n    errorThresh: self.errorThresh // error threshold to reach\n  });\n  this.flood();\n}\n\n/*\n  Every time you finish an epoch of flood,\n  you must write null to the stream\n  to let it know we have reached the end of the epoch\n */\nStreamTester.prototype.flood = function() {\n  var self = this;\n\n  for (var i = self.testData.length - 1; i >= 0; i--) {\n    self.trainStream.write(self.testData[i]);\n  }\n  self.trainStream.write(null);\n}\n\nStreamTester.prototype.doneTraining = function(info) {\n  var self = this;\n\n  for (var i in self.testData) {\n    var output = self.net.run(self.testData[i].input)[0];\n    var target = self.testData[i].output;\n    assert.ok(output < (target + self.wiggle) && output > (target - self.wiggle),\n      \"failed to train \" + self.op + \" - output: \" + output + \" target: \" + target);\n  }\n}\n\n\nfunction testBitwise(data, op) {\n  var st = StreamTester({\n    testData: data,\n    op: op,\n    wiggle: 0.1,\n    errorThresh: 0.003\n  });\n}\n\ndescribe('bitwise functions', function() {\n\n  it('NOT function', function() {\n    var not = [{\n      input: [0],\n      output: [1]\n    }, {\n      input: [1],\n      output: [0]\n    }];\n    testBitwise(not, \"not\");\n  })\n\n  it('XOR function', function() {\n    var xor = [{\n      input: [0, 0],\n      output: [0]\n    }, {\n      input: [0, 1],\n      output: [1]\n    }, {\n      input: [1, 0],\n      output: [1]\n    }, {\n      input: [1, 1],\n      output: [0]\n    }];\n    testBitwise(xor, \"xor\");\n  })\n\n  it('OR function', function() {\n    var or = [{\n      input: [0, 0],\n      output: [0]\n    }, {\n      input: [0, 1],\n      output: [1]\n    }, {\n      input: [1, 0],\n      output: [1]\n    }, {\n      input: [1, 1],\n      output: [1]\n    }];\n    testBitwise(or, \"or\");\n  });\n\n  it('AND function', function() {\n    var and = [{\n      input: [0, 0],\n      output: [0]\n    }, {\n      input: [0, 1],\n      output: [0]\n    }, {\n      input: [1, 0],\n      output: [0]\n    }, {\n      input: [1, 1],\n      output: [1]\n    }];\n    testBitwise(and, \"and\");\n  })\n})\n"
  },
  {
    "path": "test/unit/trainopts.js",
    "content": "var assert = require(\"assert\"),\n    brain = require(\"../../lib/brain\");\n\nvar data = [{input: [0, 0], output: [0]},\n            {input: [0, 1], output: [1]},\n            {input: [1, 0], output: [1]},\n            {input: [1, 1], output: [1]}];\n\ndescribe('train() options', function() {\n  it('train until error threshold reached', function() {\n    var net = new brain.NeuralNetwork();\n    var error = net.train(data, {\n      errorThresh: 0.2,\n      iterations: 100000\n    }).error;\n\n    assert.ok(error < 0.2, \"network did not train until error threshold was reached\");\n  });\n\n  it('train until max iterations reached', function() {\n    var net = new brain.NeuralNetwork();\n    var stats = net.train(data, {\n      errorThresh: 0.001,\n      iterations: 1\n    });\n\n    assert.equal(stats.iterations, 1);\n  })\n\n  it('training callback called with training stats', function(done) {\n    var iters = 100;\n    var period = 20;\n    var target = iters / 20;\n\n    var calls = 0;\n\n    var net = new brain.NeuralNetwork();\n    net.train(data, {\n      iterations: iters,\n      callback: function(stats) {\n        assert.ok(stats.iterations % period == 0);\n\n        calls++;\n        if (calls == target) {\n          done();\n        }\n      },\n      callbackPeriod: 20\n    });\n  });\n})\n"
  }
]