text: /*!
   * The MIT License (MIT)
   * 
   * Copyright 2017 Thomas Wagenaar <wagenaartje@protonmail.com>. Copyright for
   * portions of Neataptic are held by Copyright 2017 Juan Cazala - cazala.com, as a
   * part of project Synaptic.
   * 
   * 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 webpackUniversalModuleDefinition(root, factory) {
  	if(typeof exports === 'object' && typeof module === 'object')
  		module.exports = factory();
  	else if(typeof define === 'function' && define.amd)
  		define([], factory);
  	else if(typeof exports === 'object')
  		exports["neataptic"] = factory();
  	else
  		root["neataptic"] = factory();
  })(this, function() {
  return /******/ (function(modules) { // webpackBootstrap
  /******/ 	// The module cache
  /******/ 	var installedModules = {};
  /******/
  /******/ 	// The require function
  /******/ 	function __webpack_require__(moduleId) {
  /******/
  /******/ 		// Check if module is in cache
  /******/ 		if(installedModules[moduleId])
  /******/ 			return installedModules[moduleId].exports;
  /******/
  /******/ 		// Create a new module (and put it into the cache)
  /******/ 		var module = installedModules[moduleId] = {
  /******/ 			i: moduleId,
  /******/ 			l: false,
  /******/ 			exports: {}
  /******/ 		};
  /******/
  /******/ 		// Execute the module function
  /******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
  /******/
  /******/ 		// Flag the module as loaded
  /******/ 		module.l = true;
  /******/
  /******/ 		// Return the exports of the module
  /******/ 		return module.exports;
  /******/ 	}
  /******/
  /******/
  /******/ 	// expose the modules object (__webpack_modules__)
  /******/ 	__webpack_require__.m = modules;
  /******/
  /******/ 	// expose the module cache
  /******/ 	__webpack_require__.c = installedModules;
  /******/
  /******/ 	// identity function for calling harmony imports with the correct context
  /******/ 	__webpack_require__.i = function(value) { return value; };
  /******/
  /******/ 	// define getter function for harmony exports
  /******/ 	__webpack_require__.d = function(exports, name, getter) {
  /******/ 		if(!__webpack_require__.o(exports, name)) {
  /******/ 			Object.defineProperty(exports, name, {
  /******/ 				configurable: false,
  /******/ 				enumerable: true,
  /******/ 				get: getter
  /******/ 			});
  /******/ 		}
  /******/ 	};
  /******/
  /******/ 	// getDefaultExport function for compatibility with non-harmony modules
  /******/ 	__webpack_require__.n = function(module) {
  /******/ 		var getter = module && module.__esModule ?
  /******/ 			function getDefault() { return module['default']; } :
  /******/ 			function getModuleExports() { return module; };
  /******/ 		__webpack_require__.d(getter, 'a', getter);
  /******/ 		return getter;
  /******/ 	};
  /******/
  /******/ 	// Object.prototype.hasOwnProperty.call
  /******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
  /******/
  /******/ 	// __webpack_public_path__
  /******/ 	__webpack_require__.p = "";
  /******/
  /******/ 	// Load entry module and return exports
  /******/ 	return __webpack_require__(__webpack_require__.s = 21);
  /******/ })
  /************************************************************************/
  /******/ ([
  /* 0 */
  /***/ (function(module, exports, __webpack_require__) {
  
  /*******************************************************************************
                                    METHODS
  *******************************************************************************/
  
  var methods = {
    activation: __webpack_require__(9),
    mutation: __webpack_require__(16),
    selection: __webpack_require__(18),
    crossover: __webpack_require__(14),
    cost: __webpack_require__(13),
    gating: __webpack_require__(15),
    connection: __webpack_require__(12),
    rate: __webpack_require__(17)
  };
  
  /** Export */
  module.exports = methods;
  
  
  /***/ }),
  /* 1 */
  /***/ (function(module, exports, __webpack_require__) {
  
  /* Export */
  module.exports = Node;
  
  /* Import */
  var methods = __webpack_require__(0);
  var Connection = __webpack_require__(3);
  var config = __webpack_require__(2);
  
  /*******************************************************************************
                                           NODE
  *******************************************************************************/
  
  function Node (type) {
    this.bias = (type === 'input') ? 0 : Math.random() * 0.2 - 0.1;
    this.squash = methods.activation.LOGISTIC;
    this.type = type || 'hidden';
  
    this.activation = 0;
    this.state = 0;
    this.old = 0;
  
    // For dropout
    this.mask = 1;
  
    // For tracking momentum
    this.previousDeltaBias = 0;
  
    // Batch training
    this.totalDeltaBias = 0;
  
    this.connections = {
      in: [],
      out: [],
      gated: [],
      self: new Connection(this, this, 0)
    };
  
    // Data for backpropagation
    this.error = {
      responsibility: 0,
      projected: 0,
      gated: 0
    };
  }
  
  Node.prototype = {
    /**
     * Activates the node
     */
    activate: function (input) {
      // Check if an input is given
      if (typeof input !== 'undefined') {
        this.activation = input;
        return this.activation;
      }
  
      this.old = this.state;
  
      // All activation sources coming from the node itself
      this.state = this.connections.self.gain * this.connections.self.weight * this.state + this.bias;
  
      // Activation sources coming from connections
      var i;
      for (i = 0; i < this.connections.in.length; i++) {
        var connection = this.connections.in[i];
        this.state += connection.from.activation * connection.weight * connection.gain;
      }
  
      // Squash the values received
      this.activation = this.squash(this.state) * this.mask;
      this.derivative = this.squash(this.state, true);
  
      // Update traces
      var nodes = [];
      var influences = [];
  
      for (i = 0; i < this.connections.gated.length; i++) {
        let conn = this.connections.gated[i];
        let node = conn.to;
  
        let index = nodes.indexOf(node);
        if (index > -1) {
          influences[index] += conn.weight * conn.from.activation;
        } else {
          nodes.push(node);
          influences.push(conn.weight * conn.from.activation +
            (node.connections.self.gater === this ? node.old : 0));
        }
  
        // Adjust the gain to this nodes' activation
        conn.gain = this.activation;
      }
  
      for (i = 0; i < this.connections.in.length; i++) {
        let connection = this.connections.in[i];
  
        // Elegibility trace
        connection.elegibility = this.connections.self.gain * this.connections.self.weight *
          connection.elegibility + connection.from.activation * connection.gain;
  
        // Extended trace
        for (var j = 0; j < nodes.length; j++) {
          let node = nodes[j];
          let influence = influences[j];
  
          let index = connection.xtrace.nodes.indexOf(node);
  
          if (index > -1) {
            connection.xtrace.values[index] = node.connections.self.gain * node.connections.self.weight *
              connection.xtrace.values[index] + this.derivative * connection.elegibility * influence;
          } else {
            // Does not exist there yet, might be through mutation
            connection.xtrace.nodes.push(node);
            connection.xtrace.values.push(this.derivative * connection.elegibility * influence);
          }
        }
      }
  
      return this.activation;
    },
  
    /**
     * Back-propagate the error, aka learn
     */
    propagate: function (rate, momentum, update, target) {
      momentum = momentum || 0;
      rate = rate || 0.3;
  
      // Error accumulator
      var error = 0;
  
      // Output nodes get their error from the enviroment
      if (this.type === 'output') {
        this.error.responsibility = this.error.projected = target - this.activation;
      } else { // the rest of the nodes compute their error responsibilities by backpropagation
        // error responsibilities from all the connections projected from this node
        var i;
        for (i = 0; i < this.connections.out.length; i++) {
          let connection = this.connections.out[i];
          let node = connection.to;
          // Eq. 21
          error += node.error.responsibility * connection.weight * connection.gain;
        }
  
        // Projected error responsibility
        this.error.projected = this.derivative * error;
  
        // Error responsibilities from all connections gated by this neuron
        error = 0;
  
        for (i = 0; i < this.connections.gated.length; i++) {
          let conn = this.connections.gated[i];
          let node = conn.to;
          let influence = node.connections.self.gater === this ? node.old : 0;
  
          influence += conn.weight * conn.from.activation;
          error += node.error.responsibility * influence;
        }
  
        // Gated error responsibility
        this.error.gated = this.derivative * error;
  
        // Error responsibility
        this.error.responsibility = this.error.projected + this.error.gated;
      }
  
      if (this.type === 'constant') return;
  
      // Adjust all the node's incoming connections
      for (i = 0; i < this.connections.in.length; i++) {
        let connection = this.connections.in[i];
  
        let gradient = this.error.projected * connection.elegibility;
  
        for (var j = 0; j < connection.xtrace.nodes.length; j++) {
          let node = connection.xtrace.nodes[j];
          let value = connection.xtrace.values[j];
          gradient += node.error.responsibility * value;
        }
  
        // Adjust weight
        let deltaWeight = rate * gradient * this.mask;
        connection.totalDeltaWeight += deltaWeight;
        if (update) {
          connection.totalDeltaWeight += momentum * connection.previousDeltaWeight;
          connection.weight += connection.totalDeltaWeight;
          connection.previousDeltaWeight = connection.totalDeltaWeight;
          connection.totalDeltaWeight = 0;
        }
      }
  
      // Adjust bias
      var deltaBias = rate * this.error.responsibility;
      this.totalDeltaBias += deltaBias;
      if (update) {
        this.totalDeltaBias += momentum * this.previousDeltaBias;
        this.bias += this.totalDeltaBias;
        this.previousDeltaBias = this.totalDeltaBias;
        this.totalDeltaBias = 0;
      }
    },
  
    /**
     * Creates a connection from this node to the given node
     */
    connect: function (target, weight) {
      var connections = [];
      if (typeof target.bias !== 'undefined') { // must be a node!
        if (target === this) {
          // Turn on the self connection by setting the weight
          if (this.connections.self.weight !== 0) {
            if (config.warnings) console.warn('This connection already exists!');
          } else {
            this.connections.self.weight = weight || 1;
          }
          connections.push(this.connections.self);
        } else if (this.isProjectingTo(target)) {
          throw new Error('Already projecting a connection to this node!');
        } else {
          let connection = new Connection(this, target, weight);
          target.connections.in.push(connection);
          this.connections.out.push(connection);
  
          connections.push(connection);
        }
      } else { // should be a group
        for (var i = 0; i < target.nodes.length; i++) {
          let connection = new Connection(this, target.nodes[i], weight);
          target.nodes[i].connections.in.push(connection);
          this.connections.out.push(connection);
          target.connections.in.push(connection);
  
          connections.push(connection);
        }
      }
      return connections;
    },
  
    /**
     * Disconnects this node from the other node
     */
    disconnect: function (node, twosided) {
      if (this === node) {
        this.connections.self.weight = 0;
        return;
      }
  
      for (var i = 0; i < this.connections.out.length; i++) {
        let conn = this.connections.out[i];
        if (conn.to === node) {
          this.connections.out.splice(i, 1);
          let j = conn.to.connections.in.indexOf(conn);
          conn.to.connections.in.splice(j, 1);
          if (conn.gater !== null) this.ungate(conn);
          break;
        }
      }
  
      if (twosided) {
        node.disconnect(this);
      }
    },
  
    /**
     * Make this node gate a connection
     */
    gate: function (connections) {
      if (!Array.isArray(connections)) {
        connections = [connections];
      }
  
      for (var i = 0; i < connections.length; i++) {
        var connection = connections[i];
  
        this.connections.gated.push(connection);
        connection.gater = this;
      }
    },
  
    /**
     * Removes the gates from this node from the given connection(s)
     */
    ungate: function (connections) {
      if (!Array.isArray(connections)) {
        connections = [connections];
      }
  
      for (var i = connections.length - 1; i >= 0; i--) {
        var connection = connections[i];
  
        var index = this.connections.gated.indexOf(connection);
        this.connections.gated.splice(index, 1);
        connection.gater = null;
      }
    },
  
    /**
     * Clear the context of the node
     */
    clear: function () {
      for (var i = 0; i < this.connections.in.length; i++) {
        var connection = this.connections.in[i];
  
        connection.elegibility = 0;
        connection.xtrace = {
          nodes: [],
          values: []
        };
      }
  
      this.error.responsibility = this.error.projected = this.error.gated = 0;
      this.old = this.state = this.activation = 0;
    },
  
    /**
     * Mutates the node with the given method
     */
    mutate: function (method) {
      if (typeof method === 'undefined') {
        throw new Error('No mutate method given!');
      } else if (!(method.name in methods.mutation)) {
        throw new Error('This method does not exist!');
      }
  
      switch (method) {
        case methods.mutation.MOD_ACTIVATION:
          // Can't be the same squash
          var squash = method.allowed[(method.allowed.indexOf(this.squash) + Math.floor(Math.random() * (method.allowed.length - 1)) + 1) % method.allowed.length];
          this.squash = squash;
          break;
        case methods.mutation.MOD_BIAS:
          var modification = Math.random() * (method.max - method.min) + method.min;
          this.bias += modification;
          break;
      }
    },
  
    /**
     * Checks if this node is projecting to the given node
     */
    isProjectingTo: function (node) {
      for (var i = 0; i < this.connections.out.length; i++) {
        var conn = this.connections.out[i];
        if (conn.to === node) {
          return true;
        }
      }
      if (node === this && this.connections.self.weight !== 0) return true;
      return false;
    },
  
    /**
     * Checks if the given node is projecting to this node
     */
    isProjectedBy: function (node) {
      for (var i = 0; i < this.connections.in.length; i++) {
        var conn = this.connections.in[i];
        if (conn.from === node) {
          return true;
        }
      }
      if (node === this && this.connections.self.weight !== 0) return true;
      return false;
    },
  
    /**
     * Converts the node to a json object
     */
    toJSON: function () {
      var json = {
        bias: this.bias,
        type: this.type,
        squash: this.squash.name,
        mask: this.mask
      };
  
      return json;
    }
  };
  
  /**
   * Convert a json object to a node
   */
  Node.fromJSON = function (json) {
    var node = new Node();
    node.bias = json.bias;
    node.type = json.type;
    node.mask = json.mask;
  
    for (var squash in methods.activation) {
      if (methods.activation[squash].name === json.squash) {
        node.squash = methods.activation[squash];
        break;
      }
    }
  
    return node;
  };
  
  
  /***/ }),
  /* 2 */
  /***/ (function(module, exports) {
  
  /*******************************************************************************
                                        CONFIG
  *******************************************************************************/
  
  // Config
  var config = {
    warnings: false
  };
  
  /* Export */
  module.exports = config;
  
  
  /***/ }),
  /* 3 */
  /***/ (function(module, exports) {
  
  /* Export */
  module.exports = Connection;
  
  /*******************************************************************************
                                        CONNECTION
  *******************************************************************************/
  
  function Connection (from, to, weight) {
    this.from = from;
    this.to = to;
    this.gain = 1;
  
    this.weight = (typeof weight === 'undefined') ? Math.random() * 0.2 - 0.1 : weight;
  
    this.gater = null;
    this.elegibility = 0;
  
    // For tracking momentum
    this.previousDeltaWeight = 0;
  
    // Batch training
    this.totalDeltaWeight = 0;
  
    this.xtrace = {
      nodes: [],
      values: []
    };
  }
  
  Connection.prototype = {
    /**
     * Converts the connection to a json object
     */
    toJSON: function () {
      var json = {
        weight: this.weight
      };
  
      return json;
    }
  };
  
  /**
   * Returns an innovation ID
   * https://en.wikipedia.org/wiki/Pairing_function (Cantor pairing function)
   */
  Connection.innovationID = function (a, b) {
    return 1 / 2 * (a + b) * (a + b + 1) + b;
  };
  
  
  /***/ }),
  /* 4 */
  /***/ (function(module, exports, __webpack_require__) {
  
  /* Export */
  module.exports = Group;
  
  /* Import */
  var methods = __webpack_require__(0);
  var config = __webpack_require__(2);
  var Layer = __webpack_require__(5);
  var Node = __webpack_require__(1);
  
  /*******************************************************************************
                                           Group
  *******************************************************************************/
  
  function Group (size) {
    this.nodes = [];
    this.connections = {
      in: [],
      out: [],
      self: []
    };
  
    for (var i = 0; i < size; i++) {
      this.nodes.push(new Node());
    }
  }
  
  Group.prototype = {
    /**
     * Activates all the nodes in the group
     */
    activate: function (value) {
      var values = [];
  
      if (typeof value !== 'undefined' && value.length !== this.nodes.length) {
        throw new Error('Array with values should be same as the amount of nodes!');
      }
  
      for (var i = 0; i < this.nodes.length; i++) {
        var activation;
        if (typeof value === 'undefined') {
          activation = this.nodes[i].activate();
        } else {
          activation = this.nodes[i].activate(value[i]);
        }
  
        values.push(activation);
      }
  
      return values;
    },
  
    /**
     * Propagates all the node in the group
     */
    propagate: function (rate, momentum, target) {
      if (typeof target !== 'undefined' && target.length !== this.nodes.length) {
        throw new Error('Array with values should be same as the amount of nodes!');
      }
  
      for (var i = this.nodes.length - 1; i >= 0; i--) {
        if (typeof target === 'undefined') {
          this.nodes[i].propagate(rate, momentum);
        } else {
          this.nodes[i].propagate(rate, momentum, target[i]);
        }
      }
    },
  
    /**
     * Connects the nodes in this group to nodes in another group or just a node
     */
    connect: function (target, method, weight) {
      var connections = [];
      var i, j;
      if (target instanceof Group) {
        if (typeof method === 'undefined') {
          if (this !== target) {
            if (config.warnings) console.warn('No group connection specified, using ALL_TO_ALL');
            method = methods.connection.ALL_TO_ALL;
          } else {
            if (config.warnings) console.warn('No group connection specified, using ONE_TO_ONE');
            method = methods.connection.ONE_TO_ONE;
          }
        }
        if (method === methods.connection.ALL_TO_ALL || method === methods.connection.ALL_TO_ELSE) {
          for (i = 0; i < this.nodes.length; i++) {
            for (j = 0; j < target.nodes.length; j++) {
              if (method === methods.connection.ALL_TO_ELSE && this.nodes[i] === target.nodes[j]) continue;
              let connection = this.nodes[i].connect(target.nodes[j], weight);
              this.connections.out.push(connection[0]);
              target.connections.in.push(connection[0]);
              connections.push(connection[0]);
            }
          }
        } else if (method === methods.connection.ONE_TO_ONE) {
          if (this.nodes.length !== target.nodes.length) {
            throw new Error('From and To group must be the same size!');
          }
  
          for (i = 0; i < this.nodes.length; i++) {
            let connection = this.nodes[i].connect(target.nodes[i], weight);
            this.connections.self.push(connection[0]);
            connections.push(connection[0]);
          }
        }
      } else if (target instanceof Layer) {
        connections = target.input(this, method, weight);
      } else if (target instanceof Node) {
        for (i = 0; i < this.nodes.length; i++) {
          let connection = this.nodes[i].connect(target, weight);
          this.connections.out.push(connection[0]);
          connections.push(connection[0]);
        }
      }
  
      return connections;
    },
  
    /**
     * Make nodes from this group gate the given connection(s)
     */
    gate: function (connections, method) {
      if (typeof method === 'undefined') {
        throw new Error('Please specify Gating.INPUT, Gating.OUTPUT');
      }
  
      if (!Array.isArray(connections)) {
        connections = [connections];
      }
  
      var nodes1 = [];
      var nodes2 = [];
  
      var i, j;
      for (i = 0; i < connections.length; i++) {
        var connection = connections[i];
        if (!nodes1.includes(connection.from)) nodes1.push(connection.from);
        if (!nodes2.includes(connection.to)) nodes2.push(connection.to);
      }
  
      switch (method) {
        case methods.gating.INPUT:
          for (i = 0; i < nodes2.length; i++) {
            let node = nodes2[i];
            let gater = this.nodes[i % this.nodes.length];
  
            for (j = 0; j < node.connections.in.length; j++) {
              let conn = node.connections.in[j];
              if (connections.includes(conn)) {
                gater.gate(conn);
              }
            }
          }
          break;
        case methods.gating.OUTPUT:
          for (i = 0; i < nodes1.length; i++) {
            let node = nodes1[i];
            let gater = this.nodes[i % this.nodes.length];
  
            for (j = 0; j < node.connections.out.length; j++) {
              let conn = node.connections.out[j];
              if (connections.includes(conn)) {
                gater.gate(conn);
              }
            }
          }
          break;
        case methods.gating.SELF:
          for (i = 0; i < nodes1.length; i++) {
            let node = nodes1[i];
            let gater = this.nodes[i % this.nodes.length];
  
            if (connections.includes(node.connections.self)) {
              gater.gate(node.connections.self);
            }
          }
      }
    },
  
    /**
     * Sets the value of a property for every node
     */
    set: function (values) {
      for (var i = 0; i < this.nodes.length; i++) {
        if (typeof values.bias !== 'undefined') {
          this.nodes[i].bias = values.bias;
        }
  
        this.nodes[i].squash = values.squash || this.nodes[i].squash;
        this.nodes[i].type = values.type || this.nodes[i].type;
      }
    },
  
    /**
     * Disconnects all nodes from this group from another given group/node
     */
    disconnect: function (target, twosided) {
      twosided = twosided || false;
  
      // In the future, disconnect will return a connection so indexOf can be used
      var i, j, k;
      if (target instanceof Group) {
        for (i = 0; i < this.nodes.length; i++) {
          for (j = 0; j < target.nodes.length; j++) {
            this.nodes[i].disconnect(target.nodes[j], twosided);
  
            for (k = this.connections.out.length - 1; k >= 0; k--) {
              let conn = this.connections.out[k];
  
              if (conn.from === this.nodes[i] && conn.to === target.nodes[j]) {
                this.connections.out.splice(k, 1);
                break;
              }
            }
  
            if (twosided) {
              for (k = this.connections.in.length - 1; k >= 0; k--) {
                let conn = this.connections.in[k];
  
                if (conn.from === target.nodes[j] && conn.to === this.nodes[i]) {
                  this.connections.in.splice(k, 1);
                  break;
                }
              }
            }
          }
        }
      } else if (target instanceof Node) {
        for (i = 0; i < this.nodes.length; i++) {
          this.nodes[i].disconnect(target, twosided);
  
          for (j = this.connections.out.length - 1; j >= 0; j--) {
            let conn = this.connections.out[j];
  
            if (conn.from === this.nodes[i] && conn.to === target) {
              this.connections.out.splice(j, 1);
              break;
            }
          }
  
          if (twosided) {
            for (j = this.connections.in.length - 1; j >= 0; j--) {
              var conn = this.connections.in[j];
  
              if (conn.from === target && conn.to === this.nodes[i]) {
                this.connections.in.splice(j, 1);
                break;
              }
            }
          }
        }
      }
    },
  
    /**
     * Clear the context of this group
     */
    clear: function () {
      for (var i = 0; i < this.nodes.length; i++) {
        this.nodes[i].clear();
      }
    }
  };
  
  
  /***/ }),
  /* 5 */
  /***/ (function(module, exports, __webpack_require__) {
  
  /* Export */
  module.exports = Layer;
  
  /* Import */
  var methods = __webpack_require__(0);
  var Group = __webpack_require__(4);
  var Node = __webpack_require__(1);
  
  /*******************************************************************************
                                           Group
  *******************************************************************************/
  
  function Layer () {
    this.output = null;
  
    this.nodes = [];
    this.connections = { in: [],
      out: [],
      self: []
    };
  }
  
  Layer.prototype = {
    /**
     * Activates all the nodes in the group
     */
    activate: function (value) {
      var values = [];
  
      if (typeof value !== 'undefined' && value.length !== this.nodes.length) {
        throw new Error('Array with values should be same as the amount of nodes!');
      }
  
      for (var i = 0; i < this.nodes.length; i++) {
        var activation;
        if (typeof value === 'undefined') {
          activation = this.nodes[i].activate();
        } else {
          activation = this.nodes[i].activate(value[i]);
        }
  
        values.push(activation);
      }
  
      return values;
    },
  
    /**
     * Propagates all the node in the group
     */
    propagate: function (rate, momentum, target) {
      if (typeof target !== 'undefined' && target.length !== this.nodes.length) {
        throw new Error('Array with values should be same as the amount of nodes!');
      }
  
      for (var i = this.nodes.length - 1; i >= 0; i--) {
        if (typeof target === 'undefined') {
          this.nodes[i].propagate(rate, momentum);
        } else {
          this.nodes[i].propagate(rate, momentum, target[i]);
        }
      }
    },
  
    /**
     * Connects the nodes in this group to nodes in another group or just a node
     */
    connect: function (target, method, weight) {
      var connections;
      if (target instanceof Group || target instanceof Node) {
        connections = this.output.connect(target, method, weight);
      } else if (target instanceof Layer) {
        connections = target.input(this, method, weight);
      }
  
      return connections;
    },
  
    /**
     * Make nodes from this group gate the given connection(s)
     */
    gate: function (connections, method) {
      this.output.gate(connections, method);
    },
  
    /**
     * Sets the value of a property for every node
     */
    set: function (values) {
      for (var i = 0; i < this.nodes.length; i++) {
        var node = this.nodes[i];
  
        if (node instanceof Node) {
          if (typeof values.bias !== 'undefined') {
            node.bias = values.bias;
          }
  
          node.squash = values.squash || node.squash;
          node.type = values.type || node.type;
        } else if (node instanceof Group) {
          node.set(values);
        }
      }
    },
  
    /**
     * Disconnects all nodes from this group from another given group/node
     */
    disconnect: function (target, twosided) {
      twosided = twosided || false;
  
      // In the future, disconnect will return a connection so indexOf can be used
      var i, j, k;
      if (target instanceof Group) {
        for (i = 0; i < this.nodes.length; i++) {
          for (j = 0; j < target.nodes.length; j++) {
            this.nodes[i].disconnect(target.nodes[j], twosided);
  
            for (k = this.connections.out.length - 1; k >= 0; k--) {
              let conn = this.connections.out[k];
  
              if (conn.from === this.nodes[i] && conn.to === target.nodes[j]) {
                this.connections.out.splice(k, 1);
                break;
              }
            }
  
            if (twosided) {
              for (k = this.connections.in.length - 1; k >= 0; k--) {
                let conn = this.connections.in[k];
  
                if (conn.from === target.nodes[j] && conn.to === this.nodes[i]) {
                  this.connections.in.splice(k, 1);
                  break;
                }
              }
            }
          }
        }
      } else if (target instanceof Node) {
        for (i = 0; i < this.nodes.length; i++) {
          this.nodes[i].disconnect(target, twosided);
  
          for (j = this.connections.out.length - 1; j >= 0; j--) {
            let conn = this.connections.out[j];
  
            if (conn.from === this.nodes[i] && conn.to === target) {
              this.connections.out.splice(j, 1);
              break;
            }
          }
  
          if (twosided) {
            for (k = this.connections.in.length - 1; k >= 0; k--) {
              let conn = this.connections.in[k];
  
              if (conn.from === target && conn.to === this.nodes[i]) {
                this.connections.in.splice(k, 1);
                break;
              }
            }
          }
        }
      }
    },
  
    /**
     * Clear the context of this group
     */
    clear: function () {
      for (var i = 0; i < this.nodes.length; i++) {
        this.nodes[i].clear();
      }
    }
  };
  
  Layer.Dense = function (size) {
    // Create the layer
    var layer = new Layer();
  
    // Init required nodes (in activation order)
    var block = new Group(size);
  
    layer.nodes.push(block);
    layer.output = block;
  
    layer.input = function (from, method, weight) {
      if (from instanceof Layer) from = from.output;
      method = method || methods.connection.ALL_TO_ALL;
      return from.connect(block, method, weight);
    };
  
    return layer;
  };
  
  Layer.LSTM = function (size) {
    // Create the layer
    var layer = new Layer();
  
    // Init required nodes (in activation order)
    var inputGate = new Group(size);
    var forgetGate = new Group(size);
    var memoryCell = new Group(size);
    var outputGate = new Group(size);
    var outputBlock = new Group(size);
  
    inputGate.set({
      bias: 1
    });
    forgetGate.set({
      bias: 1
    });
    outputGate.set({
      bias: 1
    });
  
    // Set up internal connections
    memoryCell.connect(inputGate, methods.connection.ALL_TO_ALL);
    memoryCell.connect(forgetGate, methods.connection.ALL_TO_ALL);
    memoryCell.connect(outputGate, methods.connection.ALL_TO_ALL);
    var forget = memoryCell.connect(memoryCell, methods.connection.ONE_TO_ONE);
    var output = memoryCell.connect(outputBlock, methods.connection.ALL_TO_ALL);
  
    // Set up gates
    forgetGate.gate(forget, methods.gating.SELF);
    outputGate.gate(output, methods.gating.OUTPUT);
  
    // Add to nodes array
    layer.nodes = [inputGate, forgetGate, memoryCell, outputGate, outputBlock];
  
    // Define output
    layer.output = outputBlock;
  
    layer.input = function (from, method, weight) {
      if (from instanceof Layer) from = from.output;
      method = method || methods.connection.ALL_TO_ALL;
      var connections = [];
  
      var input = from.connect(memoryCell, method, weight);
      connections = connections.concat(input);
  
      connections = connections.concat(from.connect(inputGate, method, weight));
      connections = connections.concat(from.connect(outputGate, method, weight));
      connections = connections.concat(from.connect(forgetGate, method, weight));
  
      inputGate.gate(input, methods.gating.INPUT);
  
      return connections;
    };
  
    return layer;
  };
  
  Layer.GRU = function (size) {
    // Create the layer
    var layer = new Layer();
  
    var updateGate = new Group(size);
    var inverseUpdateGate = new Group(size);
    var resetGate = new Group(size);
    var memoryCell = new Group(size);
    var output = new Group(size);
    var previousOutput = new Group(size);
  
    previousOutput.set({
      bias: 0,
      squash: methods.activation.IDENTITY,
      type: 'constant'
    });
    memoryCell.set({
      squash: methods.activation.TANH
    });
    inverseUpdateGate.set({
      bias: 0,
      squash: methods.activation.INVERSE,
      type: 'constant'
    });
    updateGate.set({
      bias: 1
    });
    resetGate.set({
      bias: 0
    });
  
    // Update gate calculation
    previousOutput.connect(updateGate, methods.connection.ALL_TO_ALL);
  
    // Inverse update gate calculation
    updateGate.connect(inverseUpdateGate, methods.connection.ONE_TO_ONE, 1);
  
    // Reset gate calculation
    previousOutput.connect(resetGate, methods.connection.ALL_TO_ALL);
  
    // Memory calculation
    var reset = previousOutput.connect(memoryCell, methods.connection.ALL_TO_ALL);
  
    resetGate.gate(reset, methods.gating.OUTPUT); // gate
  
    // Output calculation
    var update1 = previousOutput.connect(output, methods.connection.ALL_TO_ALL);
    var update2 = memoryCell.connect(output, methods.connection.ALL_TO_ALL);
  
    updateGate.gate(update1, methods.gating.OUTPUT);
    inverseUpdateGate.gate(update2, methods.gating.OUTPUT);
  
    // Previous output calculation
    output.connect(previousOutput, methods.connection.ONE_TO_ONE, 1);
  
    // Add to nodes array
    layer.nodes = [updateGate, inverseUpdateGate, resetGate, memoryCell, output, previousOutput];
  
    layer.output = output;
  
    layer.input = function (from, method, weight) {
      if (from instanceof Layer) from = from.output;
      method = method || methods.connection.ALL_TO_ALL;
      var connections = [];
  
      connections = connections.concat(from.connect(updateGate, method, weight));
      connections = connections.concat(from.connect(resetGate, method, weight));
      connections = connections.concat(from.connect(memoryCell, method, weight));
  
      return connections;
    };
  
    return layer;
  };
  
  Layer.Memory = function (size, memory) {
    // Create the layer
    var layer = new Layer();
    // Because the output can only be one group, we have to put the nodes all in óne group
  
    var previous = null;
    var i;
    for (i = 0; i < memory; i++) {
      var block = new Group(size);
  
      block.set({
        squash: methods.activation.IDENTITY,
        bias: 0,
        type: 'constant'
      });
  
      if (previous != null) {
        previous.connect(block, methods.connection.ONE_TO_ONE, 1);
      }
  
      layer.nodes.push(block);
      previous = block;
    }
  
    layer.nodes.reverse();
  
    for (i = 0; i < layer.nodes.length; i++) {
      layer.nodes[i].nodes.reverse();
    }
  
    // Because output can only be óne group, fit all memory nodes in óne group
    var outputGroup = new Group(0);
    for (var group in layer.nodes) {
      outputGroup.nodes = outputGroup.nodes.concat(layer.nodes[group].nodes);
    }
    layer.output = outputGroup;
  
    layer.input = function (from, method, weight) {
      if (from instanceof Layer) from = from.output;
      method = method || methods.connection.ALL_TO_ALL;
  
      if (from.nodes.length !== layer.nodes[layer.nodes.length - 1].nodes.length) {
        throw new Error('Previous layer size must be same as memory size');
      }
  
      return from.connect(layer.nodes[layer.nodes.length - 1], methods.connection.ONE_TO_ONE, 1);
    };
  
    return layer;
  };
  
  
  /***/ }),
  /* 6 */
  /***/ (function(module, exports, __webpack_require__) {
  
  /* Export */
  module.exports = Network;
  
  /* Import */
  var multi = __webpack_require__(7);
  var methods = __webpack_require__(0);
  var Connection = __webpack_require__(3);
  var config = __webpack_require__(2);
  var Neat = __webpack_require__(8);
  var Node = __webpack_require__(1);
  
  /* Easier variable naming */
  var mutation = methods.mutation;
  
  /*******************************************************************************
                                   NETWORK
  *******************************************************************************/
  
  function Network (input, output) {
    if (typeof input === 'undefined' || typeof output === 'undefined') {
      throw new Error('No input or output size given');
    }
  
    this.input = input;
    this.output = output;
  
    // Store all the node and connection genes
    this.nodes = []; // Stored in activation order
    this.connections = [];
    this.gates = [];
    this.selfconns = [];
  
    // Regularization
    this.dropout = 0;
  
    // Create input and output nodes
    var i;
    for (i = 0; i < this.input + this.output; i++) {
      var type = (i < this.input) ? 'input' : 'output';
      this.nodes.push(new Node(type, this.nodes.length));
    }
  
    // Connect input nodes with output nodes directly
    for (i = 0; i < this.input; i++) {
      for (var j = this.input; j < this.output + this.input; j++) {
        // https://stats.stackexchange.com/a/248040/147931
        var weight = Math.random() * this.input * Math.sqrt(2 / this.input);
        this.connect(this.nodes[i], this.nodes[j], weight);
      }
    }
  }
  
  Network.prototype = {
    /**
     * Activates the network
     */
    activate: function (input, training) {
      var output = [];
      // Activate nodes chronologically
      for (var i = 0; i < this.nodes.length; i++) {
        if (this.nodes[i].type === 'input') {
          this.nodes[i].activate(input[i]);
        } else if (this.nodes[i].type === 'output') {
          var activation = this.nodes[i].activate();
          output.push(activation);
        } else {
          if (training) this.nodes[i].mask = Math.random() < this.dropout ? 0 : 1;
          this.nodes[i].activate();
        }
      }
      return output;
    },
  
    /**
     * Backpropagate the network
     */
    propagate: function (rate, momentum, update, target) {
      if (typeof target !== 'undefined' && target.length !== this.output) {
        throw new Error('Output target length should match network output length');
      }
  
      var targetIndex = target.length;
  
      // Propagate output nodes
      var i;
      for (i = this.nodes.length - 1; i >= this.nodes.length - this.output; i--) {
        this.nodes[i].propagate(rate, momentum, update, target[--targetIndex]);
      }
  
      // Propagate hidden and input nodes
      for (i = this.nodes.length - this.output - 1; i >= this.input; i--) {
        this.nodes[i].propagate(rate, momentum, update);
      }
    },
  
    /**
     * Clear the context of the network
     */
    clear: function () {
      for (var i = 0; i < this.nodes.length; i++) {
        this.nodes[i].clear();
      }
    },
  
    /**
     * Connects the from node to the to node
     */
    connect: function (from, to, weight) {
      var connections = from.connect(to, weight);
  
      for (var i = 0; i < connections.length; i++) {
        var connection = connections[i];
        if (from !== to) {
          this.connections.push(connection);
        } else {
          this.selfconns.push(connection);
        }
      }
  
      return connections;
    },
  
    /**
     * Disconnects the from node from the to node
     */
    disconnect: function (from, to) {
      // Delete the connection in the network's connection array
      var connections = from === to ? this.selfconns : this.connections;
  
      for (var i = 0; i < connections.length; i++) {
        var connection = connections[i];
        if (connection.from === from && connection.to === to) {
          if (connection.gater !== null) this.ungate(connection);
          connections.splice(i, 1);
          break;
        }
      }
  
      // Delete the connection at the sending and receiving neuron
      from.disconnect(to);
    },
  
    /**
     * Gate a connection with a node
     */
    gate: function (node, connection) {
      if (this.nodes.indexOf(node) === -1) {
        throw new Error('This node is not part of the network!');
      } else if (connection.gater != null) {
        if (config.warnings) console.warn('This connection is already gated!');
        return;
      }
      node.gate(connection);
      this.gates.push(connection);
    },
  
    /**
     *  Remove the gate of a connection
     */
    ungate: function (connection) {
      var index = this.gates.indexOf(connection);
      if (index === -1) {
        throw new Error('This connection is not gated!');
      }
  
      this.gates.splice(index, 1);
      connection.gater.ungate(connection);
    },
  
    /**
     *  Removes a node from the network
     */
    remove: function (node) {
      var index = this.nodes.indexOf(node);
  
      if (index === -1) {
        throw new Error('This node does not exist in the network!');
      }
  
      // Keep track of gaters
      var gaters = [];
  
      // Remove selfconnections from this.selfconns
      this.disconnect(node, node);
  
      // Get all its inputting nodes
      var inputs = [];
      for (var i = node.connections.in.length - 1; i >= 0; i--) {
        let connection = node.connections.in[i];
        if (mutation.SUB_NODE.keep_gates && connection.gater !== null && connection.gater !== node) {
          gaters.push(connection.gater);
        }
        inputs.push(connection.from);
        this.disconnect(connection.from, node);
      }
  
      // Get all its outputing nodes
      var outputs = [];
      for (i = node.connections.out.length - 1; i >= 0; i--) {
        let connection = node.connections.out[i];
        if (mutation.SUB_NODE.keep_gates && connection.gater !== null && connection.gater !== node) {
          gaters.push(connection.gater);
        }
        outputs.push(connection.to);
        this.disconnect(node, connection.to);
      }
  
      // Connect the input nodes to the output nodes (if not already connected)
      var connections = [];
      for (i = 0; i < inputs.length; i++) {
        let input = inputs[i];
        for (var j = 0; j < outputs.length; j++) {
          let output = outputs[j];
          if (!input.isProjectingTo(output)) {
            var conn = this.connect(input, output);
            connections.push(conn[0]);
          }
        }
      }
  
      // Gate random connections with gaters
      for (i = 0; i < gaters.length; i++) {
        if (connections.length === 0) break;
  
        let gater = gaters[i];
        let connIndex = Math.floor(Math.random() * connections.length);
  
        this.gate(gater, connections[connIndex]);
        connections.splice(connIndex, 1);
      }
  
      // Remove gated connections gated by this node
      for (i = node.connections.gated.length - 1; i >= 0; i--) {
        let conn = node.connections.gated[i];
        this.ungate(conn);
      }
  
      // Remove selfconnection
      this.disconnect(node, node);
  
      // Remove the node from this.nodes
      this.nodes.splice(index, 1);
    },
  
    /**
     * Mutates the network with the given method
     */
    mutate: function (method) {
      if (typeof method === 'undefined') {
        throw new Error('No (correct) mutate method given!');
      }
  
      var i, j;
      switch (method) {
        case mutation.ADD_NODE:
          // Look for an existing connection and place a node in between
          var connection = this.connections[Math.floor(Math.random() * this.connections.length)];
          var gater = connection.gater;
          this.disconnect(connection.from, connection.to);
  
          // Insert the new node right before the old connection.to
          var toIndex = this.nodes.indexOf(connection.to);
          var node = new Node('hidden', this.nodes.length);
  
          // Random squash function
          node.mutate(mutation.MOD_ACTIVATION);
  
          // Place it in this.nodes
          var minBound = Math.min(toIndex, this.nodes.length - this.output);
          this.nodes.splice(minBound, 0, node);
  
          // Now create two new connections
          var newConn1 = this.connect(connection.from, node)[0];
          var newConn2 = this.connect(node, connection.to)[0];
  
          // Check if the original connection was gated
          if (gater != null) {
            this.gate(gater, Math.random() >= 0.5 ? newConn1 : newConn2);
          }
          break;
        case mutation.SUB_NODE:
          // Check if there are nodes left to remove
          if (this.nodes.length === this.input + this.output) {
            if (config.warnings) console.warn('No more nodes left to remove!');
            break;
          }
  
          // Select a node which isn't an input or output node
          var index = Math.floor(Math.random() * (this.nodes.length - this.output - this.input) + this.input);
          this.remove(this.nodes[index]);
          break;
        case mutation.ADD_CONN:
          // Create an array of all uncreated (feedforward) connections
          var available = [];
          for (i = 0; i < this.nodes.length - this.output; i++) {
            let node1 = this.nodes[i];
            for (j = Math.max(i + 1, this.input); j < this.nodes.length; j++) {
              let node2 = this.nodes[j];
              if (!node1.isProjectingTo(node2)) available.push([node1, node2]);
            }
          }
  
          if (available.length === 0) {
            if (config.warnings) console.warn('No more connections to be made!');
            break;
          }
  
          var pair = available[Math.floor(Math.random() * available.length)];
          this.connect(pair[0], pair[1]);
          break;
        case mutation.SUB_CONN:
          // List of possible connections that can be removed
          var possible = [];
  
          for (i = 0; i < this.connections.length; i++) {
            let conn = this.connections[i];
            // Check if it is not disabling a node
            if (conn.from.connections.out.length > 1 && conn.to.connections.in.length > 1 && this.nodes.indexOf(conn.to) > this.nodes.indexOf(conn.from)) {
              possible.push(conn);
            }
          }
  
          if (possible.length === 0) {
            if (config.warnings) console.warn('No connections to remove!');
            break;
          }
  
          var randomConn = possible[Math.floor(Math.random() * possible.length)];
          this.disconnect(randomConn.from, randomConn.to);
          break;
        case mutation.MOD_WEIGHT:
          var connection = this.connections[Math.floor(Math.random() * this.connections.length)];
          var modification = Math.random() * (method.max - method.min) + method.min;
          connection.weight += modification;
          break;
        case mutation.MOD_BIAS:
          // Has no effect on input node, so they are excluded
          var index = Math.floor(Math.random() * (this.nodes.length - this.input) + this.input);
          var node = this.nodes[index];
          node.mutate(method);
          break;
        case mutation.MOD_ACTIVATION:
          // Has no effect on input node, so they are excluded
          if (!method.mutateOutput && this.input + this.output === this.nodes.length) {
            if (config.warnings) console.warn('No nodes that allow mutation of activation function');
            break;
          }
  
          var index = Math.floor(Math.random() * (this.nodes.length - (method.mutateOutput ? 0 : this.output) - this.input) + this.input);
          var node = this.nodes[index];
  
          node.mutate(method);
          break;
        case mutation.ADD_SELF_CONN:
          // Check which nodes aren't selfconnected yet
          var possible = [];
          for (i = this.input; i < this.nodes.length; i++) {
            let node = this.nodes[i];
            if (this.selfconns.indexOf(node.connections.self) === -1) {
              possible.push(node);
            }
          }
  
          if (possible.length === 0) {
            if (config.warnings) console.warn('No more self-connections to add!');
            break;
          }
  
          // Select a random node
          var node = possible[Math.floor(Math.random() * possible.length)];
  
          // Connect it to himself
          this.connect(node, node);
          break;
        case mutation.SUB_SELF_CONN:
          if (this.selfconns.length === 0) {
            if (config.warnings) console.warn('No more self-connections to remove!');
            break;
          }
          var conn = this.selfconns[Math.floor(Math.random() * this.selfconns.length)];
          this.disconnect(conn.from, conn.to);
          break;
        case mutation.ADD_GATE:
          var allconnections = this.connections.concat(this.selfconns);
  
          // Create a list of all non-gated connections
          var possible = [];
          for (i = 0; i < allconnections.length; i++) {
            let conn = allconnections[i];
            if (conn.gater === null) {
              possible.push(conn);
            }
          }
  
          if (possible.length === 0) {
            if (config.warnings) console.warn('No more connections to gate!');
            break;
          }
  
          // Select a random gater node and connection
          var node = this.nodes[Math.floor(Math.random() * this.nodes.length)];
          var conn = possible[Math.floor(Math.random() * possible.length)];
  
          // Gate the connection with the node
          this.gate(node, conn);
          break;
        case mutation.SUB_GATE:
          // Select a random gated connection
          if (this.gates.length === 0) {
            if (config.warnings) console.warn('No more connections to ungate!');
            break;
          }
  
          var index = Math.floor(Math.random() * this.gates.length);
          var gatedconn = this.gates[index];
  
          this.ungate(gatedconn);
          break;
        case mutation.ADD_BACK_CONN:
          // Create an array of all uncreated (backfed) connections
          var available = [];
          for (i = this.input; i < this.nodes.length; i++) {
            let node1 = this.nodes[i];
            for (j = this.input; j < i; j++) {
              let node2 = this.nodes[j];
              if (!node1.isProjectingTo(node2)) available.push([node1, node2]);
            }
          }
  
          if (available.length === 0) {
            if (config.warnings) console.warn('No more connections to be made!');
            break;
          }
  
          var pair = available[Math.floor(Math.random() * available.length)];
          this.connect(pair[0], pair[1]);
          break;
        case mutation.SUB_BACK_CONN:
          // List of possible connections that can be removed
          var possible = [];
  
          for (i = 0; i < this.connections.length; i++) {
            let conn = this.connections[i];
            // Check if it is not disabling a node
            if (conn.from.connections.out.length > 1 && conn.to.connections.in.length > 1 && this.nodes.indexOf(conn.from) > this.nodes.indexOf(conn.to)) {
              possible.push(conn);
            }
          }
  
          if (possible.length === 0) {
            if (config.warnings) console.warn('No connections to remove!');
            break;
          }
  
          var randomConn = possible[Math.floor(Math.random() * possible.length)];
          this.disconnect(randomConn.from, randomConn.to);
          break;
        case mutation.SWAP_NODES:
          // Has no effect on input node, so they are excluded
          if ((method.mutateOutput && this.nodes.length - this.input < 2) ||
            (!method.mutateOutput && this.nodes.length - this.input - this.output < 2)) {
            if (config.warnings) console.warn('No nodes that allow swapping of bias and activation function');
            break;
          }
  
          var index = Math.floor(Math.random() * (this.nodes.length - (method.mutateOutput ? 0 : this.output) - this.input) + this.input);
          var node1 = this.nodes[index];
          index = Math.floor(Math.random() * (this.nodes.length - (method.mutateOutput ? 0 : this.output) - this.input) + this.input);
          var node2 = this.nodes[index];
  
          var biasTemp = node1.bias;
          var squashTemp = node1.squash;
  
          node1.bias = node2.bias;
          node1.squash = node2.squash;
          node2.bias = biasTemp;
          node2.squash = squashTemp;
          break;
      }
    },
  
    /**
     * Train the given set to this network
     */
    train: function (set, options) {
      if (set[0].input.length !== this.input || set[0].output.length !== this.output) {
        throw new Error('Dataset input/output size should be same as network input/output size!');
      }
  
      options = options || {};
  
      // Warning messages
      if (typeof options.rate === 'undefined') {
        if (config.warnings) console.warn('Using default learning rate, please define a rate!');
      }
      if (typeof options.iterations === 'undefined') {
        if (config.warnings) console.warn('No target iterations given, running until error is reached!');
      }
  
      // Read the options
      var targetError = options.error || 0.05;
      var cost = options.cost || methods.cost.MSE;
      var baseRate = options.rate || 0.3;
      var dropout = options.dropout || 0;
      var momentum = options.momentum || 0;
      var batchSize = options.batchSize || 1; // online learning
      var ratePolicy = options.ratePolicy || methods.rate.FIXED();
  
      var start = Date.now();
  
      if (batchSize > set.length) {
        throw new Error('Batch size must be smaller or equal to dataset length!');
      } else if (typeof options.iterations === 'undefined' && typeof options.error === 'undefined') {
        throw new Error('At least one of the following options must be specified: error, iterations');
      } else if (typeof options.error === 'undefined') {
        targetError = -1; // run until iterations
      } else if (typeof options.iterations === 'undefined') {
        options.iterations = 0; // run until target error
      }
  
      // Save to network
      this.dropout = dropout;
  
      if (options.crossValidate) {
        let numTrain = Math.ceil((1 - options.crossValidate.testSize) * set.length);
        var trainSet = set.slice(0, numTrain);
        var testSet = set.slice(numTrain);
      }
  
      // Loops the training process
      var currentRate = baseRate;
      var iteration = 0;
      var error = 1;
  
      var i, j, x;
      while (error > targetError && (options.iterations === 0 || iteration < options.iterations)) {
        if (options.crossValidate && error <= options.crossValidate.testError) break;
  
        iteration++;
  
        // Update the rate
        currentRate = ratePolicy(baseRate, iteration);
  
        // Checks if cross validation is enabled
        if (options.crossValidate) {
          this._trainSet(trainSet, batchSize, currentRate, momentum, cost);
          if (options.clear) this.clear();
          error = this.test(testSet, cost).error;
          if (options.clear) this.clear();
        } else {
          error = this._trainSet(set, batchSize, currentRate, momentum, cost);
          if (options.clear) this.clear();
        }
  
        // Checks for options such as scheduled logs and shuffling
        if (options.shuffle) {
          for (j, x, i = set.length; i; j = Math.floor(Math.random() * i), x = set[--i], set[i] = set[j], set[j] = x);
        }
  
        if (options.log && iteration % options.log === 0) {
          console.log('iteration', iteration, 'error', error, 'rate', currentRate);
        }
  
        if (options.schedule && iteration % options.schedule.iterations === 0) {
          options.schedule.function({ error: error, iteration: iteration });
        }
      }
  
      if (options.clear) this.clear();
  
      if (dropout) {
        for (i = 0; i < this.nodes.length; i++) {
          if (this.nodes[i].type === 'hidden' || this.nodes[i].type === 'constant') {
            this.nodes[i].mask = 1 - this.dropout;
          }
        }
      }
  
      return {
        error: error,
        iterations: iteration,
        time: Date.now() - start
      };
    },
  
    /**
     * Performs one training epoch and returns the error
     * private function used in this.train
     */
    _trainSet: function (set, batchSize, currentRate, momentum, costFunction) {
      var errorSum = 0;
      for (var i = 0; i < set.length; i++) {
        var input = set[i].input;
        var target = set[i].output;
  
        var update = !!((i + 1) % batchSize === 0 || (i + 1) === set.length);
  
        var output = this.activate(input, true);
        this.propagate(currentRate, momentum, update, target);
  
        errorSum += costFunction(target, output);
      }
      return errorSum / set.length;
    },
  
    /**
     * Tests a set and returns the error and elapsed time
     */
    test: function (set, cost) {
      // Check if dropout is enabled, set correct mask
      var i;
      if (this.dropout) {
        for (i = 0; i < this.nodes.length; i++) {
          if (this.nodes[i].type === 'hidden' || this.nodes[i].type === 'constant') {
            this.nodes[i].mask = 1 - this.dropout;
          }
        }
      }
  
      cost = cost || methods.cost.MSE;
      var error = 0;
      var start = Date.now();
  
      for (i = 0; i < set.length; i++) {
        let input = set[i].input;
        let target = set[i].output;
        let output = this.activate(input);
        error += cost(target, output);
      }
  
      error /= set.length;
  
      var results = {
        error: error,
        time: Date.now() - start
      };
  
      return results;
    },
  
    /**
     * Creates a json that can be used to create a graph with d3 and webcola
     */
    graph: function (width, height) {
      var input = 0;
      var output = 0;
  
      var json = {
        nodes: [],
        links: [],
        constraints: [{
          type: 'alignment',
          axis: 'x',
          offsets: []
        }, {
          type: 'alignment',
          axis: 'y',
          offsets: []
        }]
      };
  
      var i;
      for (i = 0; i < this.nodes.length; i++) {
        var node = this.nodes[i];
  
        if (node.type === 'input') {
          if (this.input === 1) {
            json.constraints[0].offsets.push({
              node: i,
              offset: 0
            });
          } else {
            json.constraints[0].offsets.push({
              node: i,
              offset: 0.8 * width / (this.input - 1) * input++
            });
          }
          json.constraints[1].offsets.push({
            node: i,
            offset: 0
          });
        } else if (node.type === 'output') {
          if (this.output === 1) {
            json.constraints[0].offsets.push({
              node: i,
              offset: 0
            });
          } else {
            json.constraints[0].offsets.push({
              node: i,
              offset: 0.8 * width / (this.output - 1) * output++
            });
          }
          json.constraints[1].offsets.push({
            node: i,
            offset: -0.8 * height
          });
        }
  
        json.nodes.push({
          id: i,
          name: node.type === 'hidden' ? node.squash.name : node.type.toUpperCase(),
          activation: node.activation,
          bias: node.bias
        });
      }
  
      var connections = this.connections.concat(this.selfconns);
      for (i = 0; i < connections.length; i++) {
        var connection = connections[i];
        if (connection.gater == null) {
          json.links.push({
            source: this.nodes.indexOf(connection.from),
            target: this.nodes.indexOf(connection.to),
            weight: connection.weight
          });
        } else {
          // Add a gater 'node'
          var index = json.nodes.length;
          json.nodes.push({
            id: index,
            activation: connection.gater.activation,
            name: 'GATE'
          });
          json.links.push({
            source: this.nodes.indexOf(connection.from),
            target: index,
            weight: 1 / 2 * connection.weight
          });
          json.links.push({
            source: index,
            target: this.nodes.indexOf(connection.to),
            weight: 1 / 2 * connection.weight
          });
          json.links.push({
            source: this.nodes.indexOf(connection.gater),
            target: index,
            weight: connection.gater.activation,
            gate: true
          });
        }
      }
  
      return json;
    },
  
    /**
     * Convert the network to a json object
     */
    toJSON: function () {
      var json = {
        nodes: [],
        connections: [],
        input: this.input,
        output: this.output,
        dropout: this.dropout
      };
  
      // So we don't have to use expensive .indexOf()
      var i;
      for (i = 0; i < this.nodes.length; i++) {
        this.nodes[i].index = i;
      }
  
      for (i = 0; i < this.nodes.length; i++) {
        let node = this.nodes[i];
        let tojson = node.toJSON();
        tojson.index = i;
        json.nodes.push(tojson);
  
        if (node.connections.self.weight !== 0) {
          let tojson = node.connections.self.toJSON();
          tojson.from = i;
          tojson.to = i;
  
          tojson.gater = node.connections.self.gater != null ? node.connections.self.gater.index : null;
          json.connections.push(tojson);
        }
      }
  
      for (i = 0; i < this.connections.length; i++) {
        let conn = this.connections[i];
        let tojson = conn.toJSON();
        tojson.from = conn.from.index;
        tojson.to = conn.to.index;
  
        tojson.gater = conn.gater != null ? conn.gater.index : null;
  
        json.connections.push(tojson);
      }
  
      return json;
    },
  
    /**
     * Sets the value of a property for every node in this network
     */
    set: function (values) {
      for (var i = 0; i < this.nodes.length; i++) {
        this.nodes[i].bias = values.bias || this.nodes[i].bias;
        this.nodes[i].squash = values.squash || this.nodes[i].squash;
      }
    },
  
    /**
     * Evolves the network to reach a lower error on a dataset
     */
    evolve: async function (set, options) {
      if (set[0].input.length !== this.input || set[0].output.length !== this.output) {
        throw new Error('Dataset input/output size should be same as network input/output size!');
      }
  
      // Read the options
      options = options || {};
      var targetError = typeof options.error !== 'undefined' ? options.error : 0.05;
      var growth = typeof options.growth !== 'undefined' ? options.growth : 0.0001;
      var cost = options.cost || methods.cost.MSE;
      var threads = options.threads || (typeof navigator === 'undefined' ? 1 : navigator.hardwareConcurrency);
      var amount = options.amount || 1;
  
      if (threads > 1 && set[0].input.length + set[0].output.length < 100) {
        console.warn(
          `Multithreading is automatically enabled, but for small datasets, we
          encourage using just 1 thread!`
        );
      }
  
      var start = Date.now();
  
      if (typeof options.iterations === 'undefined' && typeof options.error === 'undefined') {
        throw new Error('At least one of the following options must be specified: error, iterations');
      } else if (typeof options.error === 'undefined') {
        targetError = -1; // run until iterations
      } else if (typeof options.iterations === 'undefined') {
        options.iterations = 0; // run until target error
      }
  
      var fitnessFunction;
      if (threads === 1) {
        // Create the fitness function
        fitnessFunction = function (genome) {
          var score = 0;
          for (var i = 0; i < amount; i++) {
            score -= genome.test(set, cost).error;
          }
  
          score -= (genome.nodes.length - genome.input - genome.output + genome.connections.length + genome.gates.length) * growth;
          score = isNaN(score) ? -Infinity : score; // this can cause problems with fitness proportionate selection
  
          return score / amount;
        };
      } else {
        if (typeof window === 'undefined') {
          throw new Error('Multithreading is not yet supported by Neataptic for Node.js');
        }
  
        // Serialize the dataset
        var converted = multi.serializeDataSet(set);
  
        // Create workers, send datasets
        var workers = [];
        for (var i = 0; i < threads; i++) {
          workers.push(new multi.workers.TestWorker(converted, cost));
        }
  
        fitnessFunction = function (population) {
          return new Promise((resolve, reject) => {
            // Create a queue
            var queue = neat.population.slice();
            var done = 0;
  
            // Start worker function
            var startWorker = function (worker) {
              if (!queue.length) {
                if (++done === threads) resolve();
                return;
              }
  
              var genome = queue.shift();
  
              worker.evaluate(genome).then(function (result) {
                genome.score = -result;
                genome.score -= (genome.nodes.length - genome.input - genome.output +
                  genome.connections.length + genome.gates.length) * growth;
                startWorker(worker);
              });
            };
  
            for (var i = 0; i < workers.length; i++) {
              startWorker(workers[i]);
            }
          });
        };
  
        options.fitnessPopulation = true;
      }
  
      // Intialise the NEAT instance
      options.network = this;
      var neat = new Neat(this.input, this.output, fitnessFunction, options);
  
      var error = -Infinity;
      var bestFitness = -Infinity;
      var bestGenome;
  
      while (error < -targetError && (options.iterations === 0 || neat.generation < options.iterations)) {
        let fittest = await neat.evolve();
        let fitness = fittest.score;
        error = fitness + (fittest.nodes.length - fittest.input - fittest.output + fittest.connections.length + fittest.gates.length) * growth;
  
        if (fitness > bestFitness) {
          bestFitness = fitness;
          bestGenome = fittest;
        }
  
        if (options.log && neat.generation % options.log === 0) {
          console.log('iteration', neat.generation, 'fitness', fitness, 'error', -error);
        }
  
        if (options.schedule && neat.generation % options.schedule.iterations === 0) {
          options.schedule.function({ fitness: fitness, error: -error, iteration: neat.generation });
        }
      }
  
      if(threads > 1){
        for(var i = 0; i < workers.length; i++) workers[i].terminate();
      }
  
      if (typeof bestGenome !== 'undefined') {
        for (i in bestGenome) this[i] = bestGenome[i];
        if (options.clear) this.clear();
      }
  
      return {
        error: -error,
        iterations: neat.generation,
        time: Date.now() - start
      };
    },
  
    /**
     * Creates a standalone function of the network which can be run without the
     * need of a library
     */
    standalone: function () {
      var present = [];
      var activations = [];
      var states = [];
      var lines = [];
      var functions = [];
  
      var i;
      for (i = 0; i < this.input; i++) {
        var node = this.nodes[i];
        activations.push(node.activation);
        states.push(node.state);
      }
  
      lines.push('for(var i = 0; i < input.length; i++) A[i] = input[i];');
  
      // So we don't have to use expensive .indexOf()
      for (i = 0; i < this.nodes.length; i++) {
        this.nodes[i].index = i;
      }
  
      for (i = this.input; i < this.nodes.length; i++) {
        let node = this.nodes[i];
        activations.push(node.activation);
        states.push(node.state);
  
        var functionIndex = present.indexOf(node.squash.name);
  
        if (present.indexOf(node.squash.name) === -1) {
          functionIndex = present.length;
          present.push(node.squash.name);
          functions.push(node.squash.toString());
        }
  
        var incoming = [];
        for (var j = 0; j < node.connections.in.length; j++) {
          var conn = node.connections.in[j];
          var computation = `A[${conn.from.index}] * ${conn.weight}`;
  
          if (conn.gater != null) {
            computation += ` * A[${conn.gater.index}]`;
          }
  
          incoming.push(computation);
        }
  
        if (node.connections.self.weight) {
          let conn = node.connections.self;
          let computation = `S[${i}] * ${conn.weight}`;
  
          if (conn.gater != null) {
            computation += ` * A[${conn.gater.index}]`;
          }
  
          incoming.push(computation);
        }
  
        var line1 = `S[${i}] = ${incoming.join(' + ')} + ${node.bias};`;
        var line2 = `A[${i}] = F[${functionIndex}](S[${i}])${!node.mask ? ' * ' + node.mask : ''};`;
        lines.push(line1);
        lines.push(line2);
      }
  
      var output = [];
      for (i = this.nodes.length - this.output; i < this.nodes.length; i++) {
        output.push(`A[${i}]`);
      }
  
      output = `return [${output.join(',')}];`;
      lines.push(output);
  
      var total = '';
      total += `var F = [${functions.toString()}];\r\n`;
      total += `var A = [${activations.toString()}];\r\n`;
      total += `var S = [${states.toString()}];\r\n`;
      total += `function activate(input){\r\n${lines.join('\r\n')}\r\n}`;
  
      return total;
    },
  
    /**
     * Serialize to send to workers efficiently
     */
    serialize: function () {
      var activations = [];
      var states = [];
      var conns = [];
      var squashes = [
        'LOGISTIC', 'TANH', 'IDENTITY', 'STEP', 'RELU', 'SOFTSIGN', 'SINUSOID',
        'GAUSSIAN', 'BENT_IDENTITY', 'BIPOLAR', 'BIPOLAR_SIGMOID', 'HARD_TANH',
        'ABSOLUTE', 'INVERSE', 'SELU'
      ];
  
      conns.push(this.input);
      conns.push(this.output);
  
      var i;
      for (i = 0; i < this.nodes.length; i++) {
        let node = this.nodes[i];
        node.index = i;
        activations.push(node.activation);
        states.push(node.state);
      }
  
      for (i = this.input; i < this.nodes.length; i++) {
        let node = this.nodes[i];
        conns.push(node.index);
        conns.push(node.bias);
        conns.push(squashes.indexOf(node.squash.name));
  
        for (var j = 0; j < node.connections.in.length; j++) {
          let conn = node.connections.in[j];
  
          conns.push(conn.from.index);
          conns.push(conn.weight);
          conns.push(conn.gater == null ? -1 : conn.gater.index);
        }
        conns.push(-2); // stop token -> next node
      }
  
      // Convert to Float64Arrays
      activations = new Float64Array(activations);
      states = new Float64Array(states);
      conns = new Float64Array(conns);
  
      return [activations, states, conns];
    }
  };
  
  /**
   * Convert a json object to a network
   */
  Network.fromJSON = function (json) {
    var network = new Network(json.input, json.output);
    network.dropout = json.dropout;
    network.nodes = [];
    network.connections = [];
  
    var i;
    for (i = 0; i < json.nodes.length; i++) {
      network.nodes.push(Node.fromJSON(json.nodes[i]));
    }
  
    for (i = 0; i < json.connections.length; i++) {
      var conn = json.connections[i];
  
      var connection = network.connect(network.nodes[conn.from], network.nodes[conn.to])[0];
      connection.weight = conn.weight;
  
      if (conn.gater != null) {
        network.gate(network.nodes[conn.gater], connection);
      }
    }
  
    return network;
  };
  
  /**
   * Merge two networks into one
   */
  Network.merge = function (network1, network2) {
    // Create a copy of the networks
    network1 = Network.fromJSON(network1.toJSON());
    network2 = Network.fromJSON(network2.toJSON());
  
    // Check if output and input size are the same
    if (network1.output !== network2.input) {
      throw new Error('Output size of network1 should be the same as the input size of network2!');
    }
  
    // Redirect all connections from network2 input from network1 output
    var i;
    for (i = 0; i < network2.connections.length; i++) {
      let conn = network2.connections[i];
      if (conn.from.type === 'input') {
        let index = network2.nodes.indexOf(conn.from);
  
        // redirect
        conn.from = network1.nodes[network1.nodes.length - 1 - index];
      }
    }
  
    // Delete input nodes of network2
    for (i = network2.input - 1; i >= 0; i--) {
      network2.nodes.splice(i, 1);
    }
  
    // Change the node type of network1's output nodes (now hidden)
    for (i = network1.nodes.length - network1.output; i < network1.nodes.length; i++) {
      network1.nodes[i].type = 'hidden';
    }
  
    // Create one network from both networks
    network1.connections = network1.connections.concat(network2.connections);
    network1.nodes = network1.nodes.concat(network2.nodes);
  
    return network1;
  };
  
  /**
   * Create an offspring from two parent networks
   */
  Network.crossOver = function (network1, network2, equal) {
    if (network1.input !== network2.input || network1.output !== network2.output) {
      throw new Error("Networks don't have the same input/output size!");
    }
  
    // Initialise offspring
    var offspring = new Network(network1.input, network1.output);
    offspring.connections = [];
    offspring.nodes = [];
  
    // Save scores and create a copy
    var score1 = network1.score || 0;
    var score2 = network2.score || 0;
  
    // Determine offspring node size
    var size;
    if (equal || score1 === score2) {
      let max = Math.max(network1.nodes.length, network2.nodes.length);
      let min = Math.min(network1.nodes.length, network2.nodes.length);
      size = Math.floor(Math.random() * (max - min + 1) + min);
    } else if (score1 > score2) {
      size = network1.nodes.length;
    } else {
      size = network2.nodes.length;
    }
  
    // Rename some variables for easier reading
    var outputSize = network1.output;
  
    // Set indexes so we don't need indexOf
    var i;
    for (i = 0; i < network1.nodes.length; i++) {
      network1.nodes[i].index = i;
    }
  
    for (i = 0; i < network2.nodes.length; i++) {
      network2.nodes[i].index = i;
    }
  
    // Assign nodes from parents to offspring
    for (i = 0; i < size; i++) {
      // Determine if an output node is needed
      var node;
      if (i < size - outputSize) {
        let random = Math.random();
        node = random >= 0.5 ? network1.nodes[i] : network2.nodes[i];
        let other = random < 0.5 ? network1.nodes[i] : network2.nodes[i];
  
        if (typeof node === 'undefined' || node.type === 'output') {
          node = other;
        }
      } else {
        if (Math.random() >= 0.5) {
          node = network1.nodes[network1.nodes.length + i - size];
        } else {
          node = network2.nodes[network2.nodes.length + i - size];
        }
      }
  
      var newNode = new Node();
      newNode.bias = node.bias;
      newNode.squash = node.squash;
      newNode.type = node.type;
  
      offspring.nodes.push(newNode);
    }
  
    // Create arrays of connection genes
    var n1conns = {};
    var n2conns = {};
  
    // Normal connections
    for (i = 0; i < network1.connections.length; i++) {
      let conn = network1.connections[i];
      let data = {
        weight: conn.weight,
        from: conn.from.index,
        to: conn.to.index,
        gater: conn.gater != null ? conn.gater.index : -1
      };
      n1conns[Connection.innovationID(data.from, data.to)] = data;
    }
  
    // Selfconnections
    for (i = 0; i < network1.selfconns.length; i++) {
      let conn = network1.selfconns[i];
      let data = {
        weight: conn.weight,
        from: conn.from.index,
        to: conn.to.index,
        gater: conn.gater != null ? conn.gater.index : -1
      };
      n1conns[Connection.innovationID(data.from, data.to)] = data;
    }
  
    // Normal connections
    for (i = 0; i < network2.connections.length; i++) {
      let conn = network2.connections[i];
      let data = {
        weight: conn.weight,
        from: conn.from.index,
        to: conn.to.index,
        gater: conn.gater != null ? conn.gater.index : -1
      };
      n2conns[Connection.innovationID(data.from, data.to)] = data;
    }
  
    // Selfconnections
    for (i = 0; i < network2.selfconns.length; i++) {
      let conn = network2.selfconns[i];
      let data = {
        weight: conn.weight,
        from: conn.from.index,
        to: conn.to.index,
        gater: conn.gater != null ? conn.gater.index : -1
      };
      n2conns[Connection.innovationID(data.from, data.to)] = data;
    }
  
    // Split common conn genes from disjoint or excess conn genes
    var connections = [];
    var keys1 = Object.keys(n1conns);
    var keys2 = Object.keys(n2conns);
    for (i = keys1.length - 1; i >= 0; i--) {
      // Common gene
      if (typeof n2conns[keys1[i]] !== 'undefined') {
        let conn = Math.random() >= 0.5 ? n1conns[keys1[i]] : n2conns[keys1[i]];
        connections.push(conn);
  
        // Because deleting is expensive, just set it to some value
        n2conns[keys1[i]] = undefined;
      } else if (score1 >= score2 || equal) {
        connections.push(n1conns[keys1[i]]);
      }
    }
  
    // Excess/disjoint gene
    if (score2 >= score1 || equal) {
      for (i = 0; i < keys2.length; i++) {
        if (typeof n2conns[keys2[i]] !== 'undefined') {
          connections.push(n2conns[keys2[i]]);
        }
      }
    }
  
    // Add common conn genes uniformly
    for (i = 0; i < connections.length; i++) {
      let connData = connections[i];
      if (connData.to < size && connData.from < size) {
        let from = offspring.nodes[connData.from];
        let to = offspring.nodes[connData.to];
        let conn = offspring.connect(from, to)[0];
  
        conn.weight = connData.weight;
  
        if (connData.gater !== -1 && connData.gater < size) {
          offspring.gate(offspring.nodes[connData.gater], conn);
        }
      }
    }
  
    return offspring;
  };
  
  
  /***/ }),
  /* 7 */
  /***/ (function(module, exports, __webpack_require__) {
  
  /*******************************************************************************
                                  MULTITHREADING
  *******************************************************************************/
  
  var multi = {
    /** Workers */
    workers: __webpack_require__(19),
  
    /** Snippets */
    snippets: __webpack_require__(10),
  
    /** Serializes a dataset */
    serializeDataSet: function (dataSet) {
      var serialized = [dataSet[0].input.length, dataSet[0].output.length];
  
      for (var i = 0; i < dataSet.length; i++) {
        var j;
        for (j = 0; j < serialized[0]; j++) {
          serialized.push(dataSet[i].input[j]);
        }
        for (j = 0; j < serialized[1]; j++) {
          serialized.push(dataSet[i].output[j]);
        }
      }
  
      return serialized;
    }
  };
  
  /* Export */
  module.exports = multi;
  
  
  /***/ }),
  /* 8 */
  /***/ (function(module, exports, __webpack_require__) {
  
  /* Export */
  module.exports = Neat;
  
  /* Import */
  var Network = __webpack_require__(6);
  var methods = __webpack_require__(0);
  
  /* Easier variable naming */
  var selection = methods.selection;
  
  /*******************************************************************************
                                           NEAT
  *******************************************************************************/
  
  function Neat (input, output, fitness, options) {
    this.input = input; // The input size of the networks
    this.output = output; // The output size of the networks
    this.fitness = fitness; // The fitness function to evaluate the networks
  
    // Configure options
    options = options || {};
    this.equal = options.equal || false;
    this.clear = options.clear || false;
    this.popsize = options.popsize || 50;
    this.elitism = options.elitism || 0;
    this.provenance = options.provenance || 0;
    this.mutationRate = options.mutationRate || 0.3;
    this.mutationAmount = options.mutationAmount || 1;
  
    this.fitnessPopulation = options.fitnessPopulation || false;
  
    this.selection = options.selection || methods.selection.POWER;
    this.crossover = options.crossover || [
      methods.crossover.SINGLE_POINT,
      methods.crossover.TWO_POINT,
      methods.crossover.UNIFORM,
      methods.crossover.AVERAGE
    ];
    this.mutation = options.mutation || methods.mutation.FFW;
  
    this.template = options.network || false;
  
    // Generation counter
    this.generation = 0;
  
    // Initialise the genomes
    this.createPool(this.template);
  }
  
  Neat.prototype = {
    /**
     * Create the initial pool of genomes
     */
    createPool: function (network) {
      this.population = [];
  
      for (var i = 0; i < this.popsize; i++) {
        var copy;
        if (this.template) {
          copy = Network.fromJSON(network.toJSON());
        } else {
          copy = new Network(this.input, this.output);
        }
        copy.score = null;
        this.population.push(copy);
      }
    },
  
    /**
     * Evaluates, selects, breeds and mutates population
     */
    evolve: async function () {
      // Check if evaluated, sort the population
      if (this.population[this.population.length - 1].score == null) {
        await this.evaluate();
      }
      this.sort();
  
      var fittest = Network.fromJSON(this.population[0].toJSON());
      fittest.score = this.population[0].score;
  
      var newPopulation = [];
  
      // Elitism
      var elitists = [];
      for (var i = 0; i < this.elitism; i++) {
        elitists.push(this.population[i]);
      }
  
      // Provenance
      for (i = 0; i < this.provenance; i++) {
        newPopulation.push(Network.fromJSON(this.template.toJSON()));
      }
  
      // Breed the next individuals
      for (i = 0; i < this.popsize - this.elitism - this.provenance; i++) {
        newPopulation.push(this.getOffspring());
      }
  
      // Replace the old population with the new population
      this.population = newPopulation;
      this.mutate();
  
      this.population.push(...elitists);
  
      // Reset the scores
      for (i = 0; i < this.population.length; i++) {
        this.population[i].score = null;
      }
  
      this.generation++;
  
      return fittest;
    },
  
    /**
     * Breeds two parents into an offspring, population MUST be surted
     */
    getOffspring: function () {
      var parent1 = this.getParent();
      var parent2 = this.getParent();
  
      return Network.crossOver(parent1, parent2, this.equal);
    },
  
    /**
     * Mutates the given (or current) population
     */
    mutate: function () {
      // Elitist genomes should not be included
      for (var i = 0; i < this.population.length; i++) {
        if (Math.random() <= this.mutationRate) {
          for (var j = 0; j < this.mutationAmount; j++) {
            var mutationMethod = this.mutation[Math.floor(Math.random() * this.mutation.length)];
            this.population[i].mutate(mutationMethod);
          }
        }
      }
    },
  
    /**
     * Evaluates the current population
     */
    evaluate: async function () {
      if (this.fitnessPopulation) {
        await this.fitness(this.population);
      } else {
        for (var i = 0; i < this.population.length; i++) {
          var genome = this.population[i];
          if (this.clear) genome.clear();
          genome.score = await this.fitness(genome);
        }
      }
    },
  
    /**
     * Sorts the population by score
     */
    sort: function () {
      this.population.sort(function (a, b) {
        return b.score - a.score;
      });
    },
  
    /**
     * Returns the fittest genome of the current population
     */
    getFittest: function () {
      // Check if evaluated
      if (this.population[this.population.length - 1].score == null) {
        this.evaluate();
      }
  
      this.sort();
      return this.population[0];
    },
  
    /**
     * Returns the average fitness of the current population
     */
    getAverage: function () {
      if (this.population[this.population.length - 1].score == null) {
        this.evaluate();
      }
  
      var score = 0;
      for (var i = 0; i < this.population.length; i++) {
        score += this.population[i].score;
      }
  
      return score / this.population.length;
    },
  
    /**
     * Gets a genome based on the selection function
     * @return {Network} genome
     */
    getParent: function () {
      var i;
      switch (this.selection) {
        case selection.POWER:
          if (this.population[0].score < this.population[1].score) this.sort();
  
          var index = Math.floor(Math.pow(Math.random(), this.selection.power) * this.population.length);
          return this.population[index];
        case selection.FITNESS_PROPORTIONATE:
          // As negative fitnesses are possible
          // https://stackoverflow.com/questions/16186686/genetic-algorithm-handling-negative-fitness-values
          // this is unnecessarily run for every individual, should be changed
  
          var totalFitness = 0;
          var minimalFitness = 0;
          for (i = 0; i < this.population.length; i++) {
            var score = this.population[i].score;
            minimalFitness = score < minimalFitness ? score : minimalFitness;
            totalFitness += score;
          }
  
          minimalFitness = Math.abs(minimalFitness);
          totalFitness += minimalFitness * this.population.length;
  
          var random = Math.random() * totalFitness;
          var value = 0;
  
          for (i = 0; i < this.population.length; i++) {
            let genome = this.population[i];
            value += genome.score + minimalFitness;
            if (random < value) return genome;
          }
  
          // if all scores equal, return random genome
          return this.population[Math.floor(Math.random() * this.population.length)];
        case selection.TOURNAMENT:
          if (this.selection.size > this.popsize) {
            throw new Error('Your tournament size should be lower than the population size, please change methods.selection.TOURNAMENT.size');
          }
  
          // Create a tournament
          var individuals = [];
          for (i = 0; i < this.selection.size; i++) {
            let random = this.population[Math.floor(Math.random() * this.population.length)];
            individuals.push(random);
          }
  
          // Sort the tournament individuals by score
          individuals.sort(function (a, b) {
            return b.score - a.score;
          });
  
          // Select an individual
          for (i = 0; i < this.selection.size; i++) {
            if (Math.random() < this.selection.probability || i === this.selection.size - 1) {
              return individuals[i];
            }
          }
      }
    },
  
    /**
     * Export the current population to a json object
     */
    export: function () {
      var json = [];
      for (var i = 0; i < this.population.length; i++) {
        var genome = this.population[i];
        json.push(genome.toJSON());
      }
  
      return json;
    },
  
    /**
     * Import population from a json object
     */
    import: function (json) {
      var population = [];
      for (var i = 0; i < json.length; i++) {
        var genome = json[i];
        population.push(Network.fromJSON(genome));
      }
      this.population = population;
      this.popsize = population.length;
    }
  };
  
  
  /***/ }),
  /* 9 */
  /***/ (function(module, exports) {
  
  /*******************************************************************************
                                    ACTIVATION FUNCTIONS
  *******************************************************************************/
  
  // https://en.wikipedia.org/wiki/Activation_function
  // https://stats.stackexchange.com/questions/115258/comprehensive-list-of-activation-functions-in-neural-networks-with-pros-cons
  var activation = {
    LOGISTIC: function (x, derivate) {
      var fx = 1 / (1 + Math.exp(-x));
      if (!derivate) { return fx; }
      return fx * (1 - fx);
    },
    TANH: function (x, derivate) {
      if (derivate) { return 1 - Math.pow(Math.tanh(x), 2); }
      return Math.tanh(x);
    },
    IDENTITY: function (x, derivate) {
      return derivate ? 1 : x;
    },
    STEP: function (x, derivate) {
      return derivate ? 0 : x > 0 ? 1 : 0;
    },
    RELU: function (x, derivate) {
      if (derivate) { return x > 0 ? 1 : 0; }
      return x > 0 ? x : 0;
    },
    SOFTSIGN: function (x, derivate) {
      var d = 1 + Math.abs(x);
      if (derivate) { return x / Math.pow(d, 2); }
      return x / d;
    },
    SINUSOID: function (x, derivate) {
      if (derivate) { return Math.cos(x); }
      return Math.sin(x);
    },
    GAUSSIAN: function (x, derivate) {
      var d = Math.exp(-Math.pow(x, 2));
      if (derivate) { return -2 * x * d; }
      return d;
    },
    BENT_IDENTITY: function (x, derivate) {
      var d = Math.sqrt(Math.pow(x, 2) + 1);
      if (derivate) { return x / (2 * d) + 1; }
      return (d - 1) / 2 + x;
    },
    BIPOLAR: function (x, derivate) {
      return derivate ? 0 : x > 0 ? 1 : -1;
    },
    BIPOLAR_SIGMOID: function (x, derivate) {
      var d = 2 / (1 + Math.exp(-x)) - 1;
      if (derivate) { return 1 / 2 * (1 + d) * (1 - d); }
      return d;
    },
    HARD_TANH: function (x, derivate) {
      if (derivate) { return x > -1 && x < 1 ? 1 : 0; }
      return Math.max(-1, Math.min(1, x));
    },
    ABSOLUTE: function (x, derivate) {
      if (derivate) { return x < 0 ? -1 : 1; }
      return Math.abs(x);
    },
    INVERSE: function (x, derivate) {
      if (derivate) { return -1; }
      return 1 - x;
    },
    // https://arxiv.org/pdf/1706.02515.pdf
    SELU: function (x, derivate) {
      var alpha = 1.6732632423543772848170429916717;
      var scale = 1.0507009873554804934193349852946;
      var fx = x > 0 ? x : alpha * Math.exp(x) - alpha;
      if (derivate) { return x > 0 ? scale : (fx + alpha) * scale; }
      return fx * scale;
    }
  };
  
  /* Export */
  module.exports = activation;
  
  
  /***/ }),
  /* 10 */
  /***/ (function(module, exports) {
  
  /*******************************************************************************
                                    SNIPPETS
  *******************************************************************************/
  
  var snippets = {
    activations: [
      function (x) { return 1 / (1 + Math.exp(-x)); },
      function (x) { return Math.tanh(x); },
      function (x) { return x; },
      function (x) { return x > 0 ? 1 : 0; },
      function (x) { return x > 0 ? x : 0; },
      function (x) { return x / (1 + Math.abs(x)); },
      function (x) { return Math.sin(x); },
      function (x) { return Math.exp(-Math.pow(x, 2)); },
      function (x) { return (Math.sqrt(Math.pow(x, 2) + 1) - 1) / 2 + x; },
      function (x) { return x > 0 ? 1 : -1; },
      function (x) { return 2 / (1 + Math.exp(-x)) - 1; },
      function (x) { return Math.max(-1, Math.min(1, x)); },
      function (x) { return Math.abs(x); },
      function (x) { return 1 - x; },
      function (x) {
        var a = 1.6732632423543772848170429916717;
        return (x > 0 ? x : a * Math.exp(x) - a) * 1.0507009873554804934193349852946;
      }
    ],
  
    activate: function (input) {
      for (var i = 0; i < data[0]; i++) A[i] = input[i];
      for (i = 2; i < data.length; i++) {
        let index = data[i++];
        S[index] = data[i++]; // bias
        let squash = data[i++];
        while (data[i] !== -2) {
          if (index === A[data[i]]) { // selfconn
            S[index] += S[data[i++]] * data[i++] *
              (data[i++] === -1 ? 1 : A[data[i - 1]]);
          } else { // normal conn
            S[index] += A[data[i++]] * data[i++] *
              (data[i++] === -1 ? 1 : A[data[i - 1]]);
          }
        }
        A[index] = F[squash](S[index]);
      }
  
      var output = [];
      for (i = A.length - data[1]; i < A.length; i++) output.push(A[i]);
      return output;
    },
  
    testSerializedSet: function (set) {
      var inOut = set[0] + set[1];
  
      // Calculate how much samples are in the set
      var error = 0;
      for (var i = 0; i < (set.length - 2) / inOut; i++) {
        let input = [];
        for (var j = 2 + i * inOut; j < 2 + i * inOut + set[0]; j++) {
          input.push(set[j]);
        }
        let target = [];
        for (j = 2 + i * inOut + set[0]; j < 2 + i * inOut + inOut; j++) {
          target.push(set[j]);
        }
  
        let output = activate(input);
        error += cost(target, output);
      }
  
      return error / ((set.length - 2) / inOut);
    }
  };
  
  /** Export */
  module.exports = snippets;
  
  
  /***/ }),
  /* 11 */
  /***/ (function(module, exports, __webpack_require__) {
  
  /* Import */
  var methods = __webpack_require__(0);
  var Network = __webpack_require__(6);
  var Group = __webpack_require__(4);
  var Layer = __webpack_require__(5);
  var Node = __webpack_require__(1);
  
  /*******************************************************************************
                                          architect
  *******************************************************************************/
  
  var architect = {
    /**
     * Constructs a network from a given array of connected nodes
     */
    Construct: function (list) {
      // Create a network
      var network = new Network(0, 0);
  
      // Transform all groups into nodes
      var nodes = [];
  
      var i;
      for (i = 0; i < list.length; i++) {
        let j;
        if (list[i] instanceof Group) {
          for (j = 0; j < list[i].nodes.length; j++) {
            nodes.push(list[i].nodes[j]);
          }
        } else if (list[i] instanceof Layer) {
          for (j = 0; j < list[i].nodes.length; j++) {
            for (var k = 0; k < list[i].nodes[j].nodes.length; k++) {
              nodes.push(list[i].nodes[j].nodes[k]);
            }
          }
        } else if (list[i] instanceof Node) {
          nodes.push(list[i]);
        }
      }
  
      // Determine input and output nodes
      var inputs = [];
      var outputs = [];
      for (i = nodes.length - 1; i >= 0; i--) {
        if (nodes[i].type === 'output' || nodes[i].connections.out.length + nodes[i].connections.gated.length === 0) {
          nodes[i].type = 'output';
          network.output++;
          outputs.push(nodes[i]);
          nodes.splice(i, 1);
        } else if (nodes[i].type === 'input' || !nodes[i].connections.in.length) {
          nodes[i].type = 'input';
          network.input++;
          inputs.push(nodes[i]);
          nodes.splice(i, 1);
        }
      }
  
      // Input nodes are always first, output nodes are always last
      nodes = inputs.concat(nodes).concat(outputs);
  
      if (network.input === 0 || network.output === 0) {
        throw new Error('Given nodes have no clear input/output node!');
      }
  
      for (i = 0; i < nodes.length; i++) {
        let j;
        for (j = 0; j < nodes[i].connections.out.length; j++) {
          network.connections.push(nodes[i].connections.out[j]);
        }
        for (j = 0; j < nodes[i].connections.gated.length; j++) {
          network.gates.push(nodes[i].connections.gated[j]);
        }
        if (nodes[i].connections.self.weight !== 0) {
          network.selfconns.push(nodes[i].connections.self);
        }
      }
  
      network.nodes = nodes;
  
      return network;
    },
  
    /**
     * Creates a multilayer perceptron (MLP)
     */
    Perceptron: function () {
      // Convert arguments to Array
      var layers = Array.prototype.slice.call(arguments);
      if (layers.length < 3) {
        throw new Error('You have to specify at least 3 layers');
      }
  
      // Create a list of nodes/groups
      var nodes = [];
      nodes.push(new Group(layers[0]));
  
      for (var i = 1; i < layers.length; i++) {
        var layer = layers[i];
        layer = new Group(layer);
        nodes.push(layer);
        nodes[i - 1].connect(nodes[i], methods.connection.ALL_TO_ALL);
      }
  
      // Construct the network
      return architect.Construct(nodes);
    },
  
    /**
     * Creates a randomly connected network
     */
    Random: function (input, hidden, output, options) {
      options = options || {};
  
      var connections = options.connections || hidden * 2;
      var backconnections = options.backconnections || 0;
      var selfconnections = options.selfconnections || 0;
      var gates = options.gates || 0;
  
      var network = new Network(input, output);
  
      var i;
      for (i = 0; i < hidden; i++) {
        network.mutate(methods.mutation.ADD_NODE);
      }
  
      for (i = 0; i < connections - hidden; i++) {
        network.mutate(methods.mutation.ADD_CONN);
      }
  
      for (i = 0; i < backconnections; i++) {
        network.mutate(methods.mutation.ADD_BACK_CONN);
      }
  
      for (i = 0; i < selfconnections; i++) {
        network.mutate(methods.mutation.ADD_SELF_CONN);
      }
  
      for (i = 0; i < gates; i++) {
        network.mutate(methods.mutation.ADD_GATE);
      }
  
      return network;
    },
  
    /**
     * Creates a long short-term memory network
     */
    LSTM: function () {
      var args = Array.prototype.slice.call(arguments);
      if (args.length < 3) {
        throw new Error('You have to specify at least 3 layers');
      }
  
      var last = args.pop();
  
      var outputLayer;
      if (typeof last === 'number') {
        outputLayer = new Group(last);
        last = {};
      } else {
        outputLayer = new Group(args.pop()); // last argument
      }
  
      outputLayer.set({
        type: 'output'
      });
  
      var options = {};
      options.memoryToMemory = last.memoryToMemory || false;
      options.outputToMemory = last.outputToMemory || false;
      options.outputToGates = last.outputToGates || false;
      options.inputToOutput = last.inputToOutput === undefined ? true : last.inputToOutput;
      options.inputToDeep = last.inputToDeep === undefined ? true : last.inputToDeep;
  
      var inputLayer = new Group(args.shift()); // first argument
      inputLayer.set({
        type: 'input'
      });
  
      var blocks = args; // all the arguments in the middle
  
      var nodes = [];
      nodes.push(inputLayer);
  
      var previous = inputLayer;
      for (var i = 0; i < blocks.length; i++) {
        var block = blocks[i];
  
        // Init required nodes (in activation order)
        var inputGate = new Group(block);
        var forgetGate = new Group(block);
        var memoryCell = new Group(block);
        var outputGate = new Group(block);
        var outputBlock = i === blocks.length - 1 ? outputLayer : new Group(block);
  
        inputGate.set({
          bias: 1
        });
        forgetGate.set({
          bias: 1
        });
        outputGate.set({
          bias: 1
        });
  
        // Connect the input with all the nodes
        var input = previous.connect(memoryCell, methods.connection.ALL_TO_ALL);
        previous.connect(inputGate, methods.connection.ALL_TO_ALL);
        previous.connect(outputGate, methods.connection.ALL_TO_ALL);
        previous.connect(forgetGate, methods.connection.ALL_TO_ALL);
  
        // Set up internal connections
        memoryCell.connect(inputGate, methods.connection.ALL_TO_ALL);
        memoryCell.connect(forgetGate, methods.connection.ALL_TO_ALL);
        memoryCell.connect(outputGate, methods.connection.ALL_TO_ALL);
        var forget = memoryCell.connect(memoryCell, methods.connection.ONE_TO_ONE);
        var output = memoryCell.connect(outputBlock, methods.connection.ALL_TO_ALL);
  
        // Set up gates
        inputGate.gate(input, methods.gating.INPUT);
        forgetGate.gate(forget, methods.gating.SELF);
        outputGate.gate(output, methods.gating.OUTPUT);
  
        // Input to all memory cells
        if (options.inputToDeep && i > 0) {
          let input = inputLayer.connect(memoryCell, methods.connection.ALL_TO_ALL);
          inputGate.gate(input, methods.Gating.INPUT);
        }
  
        // Optional connections
        if (options.memoryToMemory) {
          let input = memoryCell.connect(memoryCell, methods.connection.ALL_TO_ELSE);
          inputGate.gate(input, methods.Gating.INPUT);
        }
  
        if (options.outputToMemory) {
          let input = outputLayer.connect(memoryCell, methods.connection.ALL_TO_ALL);
          inputGate.gate(input, methods.Gating.INPUT);
        }
  
        if (options.outputToGates) {
          outputLayer.connect(inputGate, methods.connection.ALL_TO_ALL);
          outputLayer.connect(forgetGate, methods.connection.ALL_TO_ALL);
          outputLayer.connect(outputGate, methods.connection.ALL_TO_ALL);
        }
  
        // Add to array
        nodes.push(inputGate);
        nodes.push(forgetGate);
        nodes.push(memoryCell);
        nodes.push(outputGate);
        if (i !== blocks.length - 1) nodes.push(outputBlock);
  
        previous = outputBlock;
      }
  
      // input to output direct connection
      if (options.inputToOutput) {
        inputLayer.connect(outputLayer, methods.connection.ALL_TO_ALL);
      }
  
      nodes.push(outputLayer);
      return architect.Construct(nodes);
    },
  
    /**
     * Creates a gated recurrent unit network
     */
    GRU: function () {
      var args = Array.prototype.slice.call(arguments);
      if (args.length < 3) {
        throw new Error('not enough layers (minimum 3) !!');
      }
  
      var inputLayer = new Group(args.shift()); // first argument
      var outputLayer = new Group(args.pop()); // last argument
      var blocks = args; // all the arguments in the middle
  
      var nodes = [];
      nodes.push(inputLayer);
  
      var previous = inputLayer;
      for (var i = 0; i < blocks.length; i++) {
        var layer = new Layer.GRU(blocks[i]);
        previous.connect(layer);
        previous = layer;
  
        nodes.push(layer);
      }
  
      previous.connect(outputLayer);
      nodes.push(outputLayer);
  
      return architect.Construct(nodes);
    },
  
    /**
     * Creates a hopfield network of the given size
     */
    Hopfield: function (size) {
      var input = new Group(size);
      var output = new Group(size);
  
      input.connect(output, methods.connection.ALL_TO_ALL);
  
      input.set({
        type: 'input'
      });
      output.set({
        squash: methods.activation.STEP,
        type: 'output'
      });
  
      var network = new architect.Construct([input, output]);
  
      return network;
    },
  
    /**
     * Creates a NARX network (remember previous inputs/outputs)
     */
    NARX: function (inputSize, hiddenLayers, outputSize, previousInput, previousOutput) {
      if (!Array.isArray(hiddenLayers)) {
        hiddenLayers = [hiddenLayers];
      }
  
      var nodes = [];
  
      var input = new Layer.Dense(inputSize);
      var inputMemory = new Layer.Memory(inputSize, previousInput);
      var hidden = [];
      var output = new Layer.Dense(outputSize);
      var outputMemory = new Layer.Memory(outputSize, previousOutput);
  
      nodes.push(input);
      nodes.push(outputMemory);
  
      for (var i = 0; i < hiddenLayers.length; i++) {
        var hiddenLayer = new Layer.Dense(hiddenLayers[i]);
        hidden.push(hiddenLayer);
        nodes.push(hiddenLayer);
        if (typeof hidden[i - 1] !== 'undefined') {
          hidden[i - 1].connect(hiddenLayer, methods.connection.ALL_TO_ALL);
        }
      }
  
      nodes.push(inputMemory);
      nodes.push(output);
  
      input.connect(hidden[0], methods.connection.ALL_TO_ALL);
      input.connect(inputMemory, methods.connection.ONE_TO_ONE, 1);
      inputMemory.connect(hidden[0], methods.connection.ALL_TO_ALL);
      hidden[hidden.length - 1].connect(output, methods.connection.ALL_TO_ALL);
      output.connect(outputMemory, methods.connection.ONE_TO_ONE, 1);
      outputMemory.connect(hidden[0], methods.connection.ALL_TO_ALL);
  
      input.set({
        type: 'input'
      });
      output.set({
        type: 'output'
      });
  
      return architect.Construct(nodes);
    }
  };
  
  /* Export */
  module.exports = architect;
  
  
  /***/ }),
  /* 12 */
  /***/ (function(module, exports) {
  
  /*******************************************************************************
                                      CONNECTION
  *******************************************************************************/
  
  // Specifies in what manner two groups are connected
  var connection = {
    ALL_TO_ALL: {
      name: 'OUTPUT'
    },
    ALL_TO_ELSE: {
      name: 'INPUT'
    },
    ONE_TO_ONE: {
      name: 'SELF'
    }
  };
  
  /* Export */
  module.exports = connection;
  
  
  /***/ }),
  /* 13 */
  /***/ (function(module, exports) {
  
  /*******************************************************************************
                                      COST FUNCTIONS
  *******************************************************************************/
  
  // https://en.wikipedia.org/wiki/Loss_function
  var cost = {
    // Cross entropy error
    CROSS_ENTROPY: function (target, output) {
      var error = 0;
      for (var i = 0; i < output.length; i++) {
        // Avoid negative and zero numbers, use 1e-15 http://bit.ly/2p5W29A
        error -= target[i] * Math.log(Math.max(output[i], 1e-15)) + (1 - target[i]) * Math.log(1 - Math.max(output[i], 1e-15));
      }
      return error;
    },
    // Mean Squared Error
    MSE: function (target, output) {
      var error = 0;
      for (var i = 0; i < output.length; i++) {
        error += Math.pow(target[i] - output[i], 2);
      }
  
      return error / output.length;
    },
    // Binary error
    BINARY: function (target, output) {
      var misses = 0;
      for (var i = 0; i < output.length; i++) {
        misses += Math.round(target[i] * 2) !== Math.round(output[i] * 2);
      }
  
      return misses;
    },
    // Mean Absolute Error
    MAE: function (target, output) {
      var error = 0;
      for (var i = 0; i < output.length; i++) {
        error += Math.abs(target[i] - output[i]);
      }
  
      return error / output.length;
    },
    // Mean Absolute Percentage Error
    MAPE: function (target, output) {
      var error = 0;
      for (var i = 0; i < output.length; i++) {
        error += Math.abs((output[i] - target[i]) / Math.max(target[i], 1e-15));
      }
  
      return error / output.length;
    },
    // Mean Squared Logarithmic Error
    MSLE: function (target, output) {
      var error = 0;
      for (var i = 0; i < output.length; i++) {
        error += Math.log(Math.max(target[i], 1e-15)) - Math.log(Math.max(output[i], 1e-15));
      }
  
      return error;
    },
    // Hinge loss, for classifiers
    HINGE: function (target, output) {
      var error = 0;
      for (var i = 0; i < output.length; i++) {
        error += Math.max(0, 1 - target[i] * output[i]);
      }
  
      return error;
    }
  };
  
  /* Export */
  module.exports = cost;
  
  
  /***/ }),
  /* 14 */
  /***/ (function(module, exports) {
  
  /*******************************************************************************
                                        CROSSOVER
  *******************************************************************************/
  
  // https://en.wikipedia.org/wiki/Crossover_(genetic_algorithm)
  var crossover = {
    SINGLE_POINT: {
      name: 'SINGLE_POINT',
      config: [0.4]
    },
    TWO_POINT: {
      name: 'TWO_POINT',
      config: [0.4, 0.9]
    },
    UNIFORM: {
      name: 'UNIFORM'
    },
    AVERAGE: {
      name: 'AVERAGE'
    }
  };
  
  /* Export */
  module.exports = crossover;
  
  
  /***/ }),
  /* 15 */
  /***/ (function(module, exports) {
  
  /*******************************************************************************
                                      GATING
  *******************************************************************************/
  
  // Specifies how to gate a connection between two groups of multiple neurons
  var gating = {
    OUTPUT: { // not yet implemented
      name: 'OUTPUT'
    },
    INPUT: { // not yet implemented
      name: 'INPUT'
    },
    SELF: {
      name: 'SELF'
    }
  };
  
  /* Export */
  module.exports = gating;
  
  
  /***/ }),
  /* 16 */
  /***/ (function(module, exports, __webpack_require__) {
  
  /* Import */
  var activation = __webpack_require__(9);
  
  /*******************************************************************************
                                        MUTATION
  *******************************************************************************/
  
  // https://en.wikipedia.org/wiki/mutation_(genetic_algorithm)
  var mutation = {
    ADD_NODE: {
      name: 'ADD_NODE'
    },
    SUB_NODE: {
      name: 'SUB_NODE',
      keep_gates: true
    },
    ADD_CONN: {
      name: 'ADD_CONN'
    },
    SUB_CONN: {
      name: 'REMOVE_CONN'
    },
    MOD_WEIGHT: {
      name: 'MOD_WEIGHT',
      min: -1,
      max: 1
    },
    MOD_BIAS: {
      name: 'MOD_BIAS',
      min: -1,
      max: 1
    },
    MOD_ACTIVATION: {
      name: 'MOD_ACTIVATION',
      mutateOutput: true,
      allowed: [
        activation.LOGISTIC,
        activation.TANH,
        activation.RELU,
        activation.IDENTITY,
        activation.STEP,
        activation.SOFTSIGN,
        activation.SINUSOID,
        activation.GAUSSIAN,
        activation.BENT_IDENTITY,
        activation.BIPOLAR,
        activation.BIPOLAR_SIGMOID,
        activation.HARD_TANH,
        activation.ABSOLUTE,
        activation.INVERSE,
        activation.SELU
      ]
    },
    ADD_SELF_CONN: {
      name: 'ADD_SELF_CONN'
    },
    SUB_SELF_CONN: {
      name: 'SUB_SELF_CONN'
    },
    ADD_GATE: {
      name: 'ADD_GATE'
    },
    SUB_GATE: {
      name: 'SUB_GATE'
    },
    ADD_BACK_CONN: {
      name: 'ADD_BACK_CONN'
    },
    SUB_BACK_CONN: {
      name: 'SUB_BACK_CONN'
    },
    SWAP_NODES: {
      name: 'SWAP_NODES',
      mutateOutput: true
    }
  };
  
  mutation.ALL = [
    mutation.ADD_NODE,
    mutation.SUB_NODE,
    mutation.ADD_CONN,
    mutation.SUB_CONN,
    mutation.MOD_WEIGHT,
    mutation.MOD_BIAS,
    mutation.MOD_ACTIVATION,
    mutation.ADD_GATE,
    mutation.SUB_GATE,
    mutation.ADD_SELF_CONN,
    mutation.SUB_SELF_CONN,
    mutation.ADD_BACK_CONN,
    mutation.SUB_BACK_CONN,
    mutation.SWAP_NODES
  ];
  
  mutation.FFW = [
    mutation.ADD_NODE,
    mutation.SUB_NODE,
    mutation.ADD_CONN,
    mutation.SUB_CONN,
    mutation.MOD_WEIGHT,
    mutation.MOD_BIAS,
    mutation.MOD_ACTIVATION,
    mutation.SWAP_NODES
  ];
  
  /* Export */
  module.exports = mutation;
  
  
  /***/ }),
  /* 17 */
  /***/ (function(module, exports) {
  
  /*******************************************************************************
                                        RATE
  *******************************************************************************/
  
  // https://stackoverflow.com/questions/30033096/what-is-lr-policy-in-caffe/30045244
  var rate = {
    FIXED: function () {
      var func = function (baseRate, iteration) { return baseRate; };
      return func;
    },
    STEP: function (gamma, stepSize) {
      gamma = gamma || 0.9;
      stepSize = stepSize || 100;
  
      var func = function (baseRate, iteration) {
        return baseRate * Math.pow(gamma, Math.floor(iteration / stepSize));
      };
  
      return func;
    },
    EXP: function (gamma) {
      gamma = gamma || 0.999;
  
      var func = function (baseRate, iteration) {
        return baseRate * Math.pow(gamma, iteration);
      };
  
      return func;
    },
    INV: function (gamma, power) {
      gamma = gamma || 0.001;
      power = power || 2;
  
      var func = function (baseRate, iteration) {
        return baseRate * Math.pow(1 + gamma * iteration, -power);
      };
  
      return func;
    }
  };
  
  /* Export */
  module.exports = rate;
  
  
  /***/ }),
  /* 18 */
  /***/ (function(module, exports) {
  
  /*******************************************************************************
                                        SELECTION
  *******************************************************************************/
  
  // https://en.wikipedia.org/wiki/Selection_(genetic_algorithm)
  
  var selection = {
    FITNESS_PROPORTIONATE: {
      name: 'FITNESS_PROPORTIONATE'
    },
    POWER: {
      name: 'POWER',
      power: 4
    },
    TOURNAMENT: {
      name: 'TOURNAMENT',
      size: 5,
      probability: 0.5
    }
  };
  
  /* Export */
  module.exports = selection;
  
  
  /***/ }),
  /* 19 */
  /***/ (function(module, exports, __webpack_require__) {
  
  /*******************************************************************************
                                    WORKERS
  *******************************************************************************/
  
  var workers = {
    TestWorker: __webpack_require__(20)
  };
  
  /** Export */
  module.exports = workers;
  
  
  /***/ }),
  /* 20 */
  /***/ (function(module, exports, __webpack_require__) {
  
  /* Export */
  module.exports = TestWorker;
  
  /* Import */
  var snippets = __webpack_require__(10);
  
  /*******************************************************************************
                                  WEBWORKER
  *******************************************************************************/
  
  function TestWorker (dataSet, cost) {
    var blob = new Blob([this._createBlobString(cost)]);
    this.url = window.URL.createObjectURL(blob);
    this.worker = new Worker(this.url);
  
    var data = { set: new Float64Array(dataSet).buffer };
    this.worker.postMessage(data, [data.set]);
  }
  
  TestWorker.prototype = {
    evaluate: function (network) {
      return new Promise((resolve, reject) => {
        var serialzed = network.serialize();
  
        var data = {
          activations: serialzed[0].buffer,
          states: serialzed[1].buffer,
          conns: serialzed[2].buffer
        };
  
        this.worker.onmessage = function (e) {
          var error = new Float64Array(e.data.buffer)[0];
          resolve(error);
        };
  
        this.worker.postMessage(data, [data.activations, data.states, data.conns]);
      });
    },
  
    terminate: function () {
      this.worker.terminate();
      window.URL.revokeObjectURL(this.url);
    },
  
    _createBlobString: function (cost) {
      var source = `
        var A, S, data;
        var F = [${snippets.activations.toString()}];
        var cost = ${cost.toString()};
        var test = ${snippets.testSerializedSet.toString()};
        var activate = ${snippets.activate.toString()};
        var set;
  
        this.onmessage = function (e) {
          if(typeof e.data.set === 'undefined'){
            A = new Float64Array(e.data.activations);
            S = new Float64Array(e.data.states);
            data = new Float64Array(e.data.conns);
  
            var error = test(set);
  
            var answer = { buffer: new Float64Array([error ]).buffer };
            postMessage(answer, [answer.buffer]);
          } else {
            set = new Float64Array(e.data.set);
          }
        };`;
  
      return source;
    }
  };
  
  
  /***/ }),
  /* 21 */
  /***/ (function(module, exports, __webpack_require__) {
  
  var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;var Neataptic = {
    methods: __webpack_require__(0),
    Connection: __webpack_require__(3),
    architect: __webpack_require__(11),
    Network: __webpack_require__(6),
    config: __webpack_require__(2),
    Group: __webpack_require__(4),
    Layer: __webpack_require__(5),
    Node: __webpack_require__(1),
    Neat: __webpack_require__(8),
    multi: __webpack_require__(7)
  };
  
  // CommonJS & AMD
  if (true) {
    !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = function () { return Neataptic; }.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
  				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
  }
  
  // Node.js
  if (typeof module !== 'undefined' && module.exports) {
    module.exports = Neataptic;
  }
  
  // Browser
  if (typeof window === 'object') {
    (function () {
      var old = window['neataptic'];
      Neataptic.ninja = function () {
        window['neataptic'] = old;
        return Neataptic;
      };
    })();
  
    window['neataptic'] = Neataptic;
  }
  
  
  /***/ })
  /******/ ]);
  });