diff --git a/README.md b/README.md
index 8fbf389..3744b36 100644
--- a/README.md
+++ b/README.md
@@ -9,6 +9,7 @@ The experiments are currently organized based on the language they use:
- [Elm](./elm/)
- [F#](./fsharp/)
- [Idris](./idris/)
+- [Javascript](./javascript/)
- [Lean](./lean/)
- [Lua](./lua/)
- [Purescript](./purescript/)
diff --git a/javascript/README.md b/javascript/README.md
new file mode 100644
index 0000000..a2b8d6a
--- /dev/null
+++ b/javascript/README.md
@@ -0,0 +1,5 @@
+# Javascript
+
+| Name | Description |
+| ----------------------------- | -------------------------------------------------- |
+| [clever-dots](./clever-dots/) | Half broken genetic algorithm implementation in js |
diff --git a/javascript/clever-dots/README.md b/javascript/clever-dots/README.md
new file mode 100644
index 0000000..f8cf1a2
--- /dev/null
+++ b/javascript/clever-dots/README.md
@@ -0,0 +1,13 @@
+# Clever-dots---genetic-algorithm-in-js
+I first watched Coding Bullet's tutorial, and it was AWESOME. Then i tried this like every day. This is my first succesfully attemp, and my 4th attemp overall.
+
+How-it-works:
+Basically its just a simple genetic algorithm using tools as selection , cross over and mutation
+You must wait a loooooooong time for it to train, so dont tell me that it doesnt work, until you dont actually see.
+To know its progress, wait until you got 20 "thinking" mesages in the console.
+I might add a progress bar another time
+
+
+
+Note:
+I sctually understood how it works from a flappy bird AI project on Github, so you would probably see some similarities
diff --git a/javascript/clever-dots/game.js b/javascript/clever-dots/game.js
new file mode 100644
index 0000000..a5241cf
--- /dev/null
+++ b/javascript/clever-dots/game.js
@@ -0,0 +1,61 @@
+function game() {
+ this.size = [500, 500];
+
+ this.clear = function () {
+ var c = document.getElementById("can");
+ var ctx = c.getContext("2d");
+ ctx.fillStyle = "#000000";
+ ctx.fillRect(0, 0, 1000, 1000);
+ //console.log("clearing");
+ };
+
+ this.draw = function (x, y) {
+ var c = document.getElementById("can");
+ var ctx = c.getContext("2d");
+ ctx.fillStyle = "#FFFFFF";
+ ctx.fillRect(x, y, 1, 1);
+ //console.log(y+"drawing"+x);
+ //console.log("finished drawing");
+ };
+}
+var a = new game();
+a.clear();
+var b = new population(a);
+b.reset();
+b.create_population();
+
+function redraw() {
+ let done = false;
+ let callback = null;
+ requestAnimationFrame(() => {
+ done = true;
+ if (callback !== null) callback();
+ });
+
+ return new Promise((res) => {
+ if (done) res();
+ else callback = () => res();
+ });
+}
+
+const iterationsPerFrame = 10;
+
+async function main() {
+ for (let k = 0; k < 1000; k++) {
+ await redraw();
+ a.clear();
+ console.log(`thinking: ${k}`);
+ for (let i = 0; i < 500; i++) {
+ if (i % iterationsPerFrame === 0) await redraw();
+ for (let j = 0; j < b.Population.length; j++) {
+ b.think(b.Population[j]);
+ a.draw(b.Population[j].x, b.Population[j].y);
+ }
+ }
+
+ b.evolve();
+ }
+}
+
+main();
+
diff --git a/javascript/clever-dots/index.html b/javascript/clever-dots/index.html
new file mode 100644
index 0000000..abb4118
--- /dev/null
+++ b/javascript/clever-dots/index.html
@@ -0,0 +1,22 @@
+
+
+
+ Genetic algorithm
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/javascript/clever-dots/population.js b/javascript/clever-dots/population.js
new file mode 100644
index 0000000..06c24d0
--- /dev/null
+++ b/javascript/clever-dots/population.js
@@ -0,0 +1,197 @@
+function population(game){
+ this.max = 50;
+ this.top = 4;
+ this.game = game;
+
+ if (this.max < this.top){
+ this.top = this.max;
+ }
+
+ this.Population = [];
+ this.scale = 200;
+
+ this.reset = function(){
+ this.iteration = 1;
+ this.mutateRate = 1;
+
+ this.best_population = 0;
+ this.best_fitness = 0;
+ this.best_score = 0;
+ }
+
+ this.create_population = function(){
+ this.Population = [];
+
+ for (var i=0; i= 0){
+ unit.y -= 1;
+ }
+ else if (max == 2 && unit.y <= 500){
+ unit.y += 1;
+ }
+ else if (max == 1 && unit.x <= 500){
+ unit.x += 1;
+ }
+ else if (max == 3 && unit.x >= 0){
+ unit.x -= 1;
+ }
+ //unit.round++;
+ //this.game.draw(unit.x,unit.y);
+ unit.fitness = dist(unit.x,unit.y,0,0);
+ //console.log(max+"evolving"+outputs);
+ }
+
+ this.evolve = function(){
+ var Winners = this.select();
+
+ if (this.mutateRate == 1 && Winners[0].fitness < 0){
+ this.create_population();
+ }
+ else{
+ this.mutateRate = 0.2;
+ }
+
+
+ for (var i=this.top; i not connected
+ this.squash = Neuron.squash.LOGISTIC;
+ this.neighboors = {};
+ this.bias = Math.random() * .2 - .1;
+ }
+
+ Neuron.prototype = {
+
+ // activate the neuron
+ activate: function(input) {
+ // activation from enviroment (for input neurons)
+ if (typeof input != 'undefined') {
+ this.activation = input;
+ this.derivative = 0;
+ this.bias = 0;
+ return this.activation;
+ }
+
+ // old state
+ this.old = this.state;
+
+ // eq. 15
+ this.state = this.selfconnection.gain * this.selfconnection.weight *
+ this.state + this.bias;
+
+ for (var i in this.connections.inputs) {
+ var input = this.connections.inputs[i];
+ this.state += input.from.activation * input.weight * input.gain;
+ }
+
+ // eq. 16
+ this.activation = this.squash(this.state);
+
+ // f'(s)
+ this.derivative = this.squash(this.state, true);
+
+ // update traces
+ var influences = [];
+ for (var id in this.trace.extended) {
+ // extended elegibility trace
+ var neuron = this.neighboors[id];
+
+ // if gated neuron's selfconnection is gated by this unit, the influence keeps track of the neuron's old state
+ var influence = neuron.selfconnection.gater == this ? neuron.old : 0;
+
+ // index runs over all the incoming connections to the gated neuron that are gated by this unit
+ for (var incoming in this.trace.influences[neuron.ID]) { // captures the effect that has an input connection to this unit, on a neuron that is gated by this unit
+ influence += this.trace.influences[neuron.ID][incoming].weight *
+ this.trace.influences[neuron.ID][incoming].from.activation;
+ }
+ influences[neuron.ID] = influence;
+ }
+
+ for (var i in this.connections.inputs) {
+ var input = this.connections.inputs[i];
+
+ // elegibility trace - Eq. 17
+ this.trace.elegibility[input.ID] = this.selfconnection.gain * this.selfconnection
+ .weight * this.trace.elegibility[input.ID] + input.gain * input.from
+ .activation;
+
+ for (var id in this.trace.extended) {
+ // extended elegibility trace
+ var xtrace = this.trace.extended[id];
+ var neuron = this.neighboors[id];
+ var influence = influences[neuron.ID];
+
+ // eq. 18
+ xtrace[input.ID] = neuron.selfconnection.gain * neuron.selfconnection
+ .weight * xtrace[input.ID] + this.derivative * this.trace.elegibility[
+ input.ID] * influence;
+ }
+ }
+
+ // update gated connection's gains
+ for (var connection in this.connections.gated) {
+ this.connections.gated[connection].gain = this.activation;
+ }
+
+ return this.activation;
+ },
+
+ // back-propagate the error
+ propagate: function(rate, target) {
+ // error accumulator
+ var error = 0;
+
+ // whether or not this neuron is in the output layer
+ var isOutput = typeof target != 'undefined';
+
+ // output neurons get their error from the enviroment
+ if (isOutput)
+ this.error.responsibility = this.error.projected = target - this.activation; // Eq. 10
+
+ else // the rest of the neuron compute their error responsibilities by backpropagation
+ {
+ // error responsibilities from all the connections projected from this neuron
+ for (var id in this.connections.projected) {
+ var connection = this.connections.projected[id];
+ var neuron = connection.to;
+ // Eq. 21
+ error += neuron.error.responsibility * connection.gain * connection.weight;
+ }
+
+ // projected error responsibility
+ this.error.projected = this.derivative * error;
+
+ error = 0;
+ // error responsibilities from all the connections gated by this neuron
+ for (var id in this.trace.extended) {
+ var neuron = this.neighboors[id]; // gated neuron
+ var influence = neuron.selfconnection.gater == this ? neuron.old : 0; // if gated neuron's selfconnection is gated by this neuron
+
+ // index runs over all the connections to the gated neuron that are gated by this neuron
+ for (var input in this.trace.influences[id]) { // captures the effect that the input connection of this neuron have, on a neuron which its input/s is/are gated by this neuron
+ influence += this.trace.influences[id][input].weight * this.trace.influences[
+ neuron.ID][input].from.activation;
+ }
+ // eq. 22
+ error += neuron.error.responsibility * influence;
+ }
+
+ // gated error responsibility
+ this.error.gated = this.derivative * error;
+
+ // error responsibility - Eq. 23
+ this.error.responsibility = this.error.projected + this.error.gated;
+ }
+
+ // learning rate
+ rate = rate || .1;
+
+ // adjust all the neuron's incoming connections
+ for (var id in this.connections.inputs) {
+ var input = this.connections.inputs[id];
+
+ // Eq. 24
+ var gradient = this.error.projected * this.trace.elegibility[input.ID];
+ for (var id in this.trace.extended) {
+ var neuron = this.neighboors[id];
+ gradient += neuron.error.responsibility * this.trace.extended[
+ neuron.ID][input.ID];
+ }
+ input.weight += rate * gradient; // adjust weights - aka learn
+ }
+
+ // adjust bias
+ this.bias += rate * this.error.responsibility;
+ },
+
+ project: function(neuron, weight) {
+ // self-connection
+ if (neuron == this) {
+ this.selfconnection.weight = 1;
+ return this.selfconnection;
+ }
+
+ // check if connection already exists
+ var connected = this.connected(neuron);
+ if (connected && connected.type == "projected") {
+ // update connection
+ if (typeof weight != 'undefined')
+ connected.connection.weight = weight;
+ // return existing connection
+ return connected.connection;
+ } else {
+ // create a new connection
+ var connection = new Neuron.connection(this, neuron, weight);
+ }
+
+ // reference all the connections and traces
+ this.connections.projected[connection.ID] = connection;
+ this.neighboors[neuron.ID] = neuron;
+ neuron.connections.inputs[connection.ID] = connection;
+ neuron.trace.elegibility[connection.ID] = 0;
+
+ for (var id in neuron.trace.extended) {
+ var trace = neuron.trace.extended[id];
+ trace[connection.ID] = 0;
+ }
+
+ return connection;
+ },
+
+ gate: function(connection) {
+ // add connection to gated list
+ this.connections.gated[connection.ID] = connection;
+
+ var neuron = connection.to;
+ if (!(neuron.ID in this.trace.extended)) {
+ // extended trace
+ this.neighboors[neuron.ID] = neuron;
+ var xtrace = this.trace.extended[neuron.ID] = {};
+ for (var id in this.connections.inputs) {
+ var input = this.connections.inputs[id];
+ xtrace[input.ID] = 0;
+ }
+ }
+
+ // keep track
+ if (neuron.ID in this.trace.influences)
+ this.trace.influences[neuron.ID].push(connection);
+ else
+ this.trace.influences[neuron.ID] = [connection];
+
+ // set gater
+ connection.gater = this;
+ },
+
+ // returns true or false whether the neuron is self-connected or not
+ selfconnected: function() {
+ return this.selfconnection.weight !== 0;
+ },
+
+ // returns true or false whether the neuron is connected to another neuron (parameter)
+ connected: function(neuron) {
+ var result = {
+ type: null,
+ connection: false
+ };
+
+ if (this == neuron) {
+ if (this.selfconnected()) {
+ result.type = 'selfconnection';
+ result.connection = this.selfconnection;
+ return result;
+ } else
+ return false;
+ }
+
+ for (var type in this.connections) {
+ for (var connection in this.connections[type]) {
+ var connection = this.connections[type][connection];
+ if (connection.to == neuron) {
+ result.type = type;
+ result.connection = connection;
+ return result;
+ } else if (connection.from == neuron) {
+ result.type = type;
+ result.connection = connection;
+ return result;
+ }
+ }
+ }
+
+ return false;
+ },
+
+ // clears all the traces (the neuron forgets it's context, but the connections remain intact)
+ clear: function() {
+
+ for (var trace in this.trace.elegibility)
+ this.trace.elegibility[trace] = 0;
+
+ for (var trace in this.trace.extended)
+ for (var extended in this.trace.extended[trace])
+ this.trace.extended[trace][extended] = 0;
+
+ this.error.responsibility = this.error.projected = this.error.gated = 0;
+ },
+
+ // all the connections are randomized and the traces are cleared
+ reset: function() {
+ this.clear();
+
+ for (var type in this.connections)
+ for (var connection in this.connections[type])
+ this.connections[type][connection].weight = Math.random() * .2 - .1;
+ this.bias = Math.random() * .2 - .1;
+
+ this.old = this.state = this.activation = 0;
+ },
+
+ // hardcodes the behaviour of the neuron into an optimized function
+ optimize: function(optimized, layer) {
+
+ optimized = optimized || {};
+ var store_activation = [];
+ var store_trace = [];
+ var store_propagation = [];
+ var varID = optimized.memory || 0;
+ var neurons = optimized.neurons || 1;
+ var inputs = optimized.inputs || [];
+ var targets = optimized.targets || [];
+ var outputs = optimized.outputs || [];
+ var variables = optimized.variables || {};
+ var activation_sentences = optimized.activation_sentences || [];
+ var trace_sentences = optimized.trace_sentences || [];
+ var propagation_sentences = optimized.propagation_sentences || [];
+ var layers = optimized.layers || { __count: 0, __neuron: 0 };
+
+ // allocate sentences
+ var allocate = function(store){
+ var allocated = layer in layers && store[layers.__count];
+ if (!allocated)
+ {
+ layers.__count = store.push([]) - 1;
+ layers[layer] = layers.__count;
+ }
+ };
+ allocate(activation_sentences);
+ allocate(trace_sentences);
+ allocate(propagation_sentences);
+ var currentLayer = layers.__count;
+
+ // get/reserve space in memory by creating a unique ID for a variablel
+ var getVar = function() {
+ var args = Array.prototype.slice.call(arguments);
+
+ if (args.length == 1) {
+ if (args[0] == 'target') {
+ var id = 'target_' + targets.length;
+ targets.push(varID);
+ } else
+ var id = args[0];
+ if (id in variables)
+ return variables[id];
+ return variables[id] = {
+ value: 0,
+ id: varID++
+ };
+ } else {
+ var extended = args.length > 2;
+ if (extended)
+ var value = args.pop();
+
+ var unit = args.shift();
+ var prop = args.pop();
+
+ if (!extended)
+ var value = unit[prop];
+
+ var id = prop + '_';
+ for (var property in args)
+ id += args[property] + '_';
+ id += unit.ID;
+ if (id in variables)
+ return variables[id];
+
+ return variables[id] = {
+ value: value,
+ id: varID++
+ };
+ }
+ };
+
+ // build sentence
+ var buildSentence = function() {
+ var args = Array.prototype.slice.call(arguments);
+ var store = args.pop();
+ var sentence = "";
+ for (var i in args)
+ if (typeof args[i] == 'string')
+ sentence += args[i];
+ else
+ sentence += 'F[' + args[i].id + ']';
+
+ store.push(sentence + ';');
+ };
+
+ // helper to check if an object is empty
+ var isEmpty = function(obj) {
+ for (var prop in obj) {
+ if (obj.hasOwnProperty(prop))
+ return false;
+ }
+ return true;
+ };
+
+ // characteristics of the neuron
+ var noProjections = isEmpty(this.connections.projected);
+ var noGates = isEmpty(this.connections.gated);
+ var isInput = layer == 'input' ? true : isEmpty(this.connections.inputs);
+ var isOutput = layer == 'output' ? true : noProjections && noGates;
+
+ // optimize neuron's behaviour
+ var rate = getVar('rate');
+ var activation = getVar(this, 'activation');
+ if (isInput)
+ inputs.push(activation.id);
+ else {
+ activation_sentences[currentLayer].push(store_activation);
+ trace_sentences[currentLayer].push(store_trace);
+ propagation_sentences[currentLayer].push(store_propagation);
+ var old = getVar(this, 'old');
+ var state = getVar(this, 'state');
+ var bias = getVar(this, 'bias');
+ if (this.selfconnection.gater)
+ var self_gain = getVar(this.selfconnection, 'gain');
+ if (this.selfconnected())
+ var self_weight = getVar(this.selfconnection, 'weight');
+ buildSentence(old, ' = ', state, store_activation);
+ if (this.selfconnected())
+ if (this.selfconnection.gater)
+ buildSentence(state, ' = ', self_gain, ' * ', self_weight, ' * ',
+ state, ' + ', bias, store_activation);
+ else
+ buildSentence(state, ' = ', self_weight, ' * ', state, ' + ',
+ bias, store_activation);
+ else
+ buildSentence(state, ' = ', bias, store_activation);
+ for (var i in this.connections.inputs) {
+ var input = this.connections.inputs[i];
+ var input_activation = getVar(input.from, 'activation');
+ var input_weight = getVar(input, 'weight');
+ if (input.gater)
+ var input_gain = getVar(input, 'gain');
+ if (this.connections.inputs[i].gater)
+ buildSentence(state, ' += ', input_activation, ' * ',
+ input_weight, ' * ', input_gain, store_activation);
+ else
+ buildSentence(state, ' += ', input_activation, ' * ',
+ input_weight, store_activation);
+ }
+ var derivative = getVar(this, 'derivative');
+ switch (this.squash) {
+ case Neuron.squash.LOGISTIC:
+ buildSentence(activation, ' = (1 / (1 + Math.exp(-', state, ')))',
+ store_activation);
+ buildSentence(derivative, ' = ', activation, ' * (1 - ',
+ activation, ')', store_activation);
+ break;
+ case Neuron.squash.TANH:
+ var eP = getVar('aux');
+ var eN = getVar('aux_2');
+ buildSentence(eP, ' = Math.exp(', state, ')', store_activation);
+ buildSentence(eN, ' = 1 / ', eP, store_activation);
+ buildSentence(activation, ' = (', eP, ' - ', eN, ') / (', eP, ' + ', eN, ')', store_activation);
+ buildSentence(derivative, ' = 1 - (', activation, ' * ', activation, ')', store_activation);
+ break;
+ case Neuron.squash.IDENTITY:
+ buildSentence(activation, ' = ', state, store_activation);
+ buildSentence(derivative, ' = 1', store_activation);
+ break;
+ case Neuron.squash.HLIM:
+ buildSentence(activation, ' = +(', state, ' > 0)', store_activation);
+ buildSentence(derivative, ' = 1', store_activation);
+ case Neuron.squash.RELU:
+ buildSentence(activation, ' = ', state, ' > 0 ? ', state, ' : 0', store_activation);
+ buildSentence(derivative, ' = ', state, ' > 0 ? 1 : 0', store_activation);
+ break;
+ }
+
+ for (var id in this.trace.extended) {
+ // calculate extended elegibility traces in advance
+
+ var neuron = this.neighboors[id];
+ var influence = getVar('influences[' + neuron.ID + ']');
+ var neuron_old = getVar(neuron, 'old');
+ var initialized = false;
+ if (neuron.selfconnection.gater == this)
+ {
+ buildSentence(influence, ' = ', neuron_old, store_trace);
+ initialized = true;
+ }
+ for (var incoming in this.trace.influences[neuron.ID]) {
+ var incoming_weight = getVar(this.trace.influences[neuron.ID]
+ [incoming], 'weight');
+ var incoming_activation = getVar(this.trace.influences[neuron.ID]
+ [incoming].from, 'activation');
+
+ if (initialized)
+ buildSentence(influence, ' += ', incoming_weight, ' * ', incoming_activation, store_trace);
+ else {
+ buildSentence(influence, ' = ', incoming_weight, ' * ', incoming_activation, store_trace);
+ initialized = true;
+ }
+ }
+ }
+
+ for (var i in this.connections.inputs) {
+ var input = this.connections.inputs[i];
+ if (input.gater)
+ var input_gain = getVar(input, 'gain');
+ var input_activation = getVar(input.from, 'activation');
+ var trace = getVar(this, 'trace', 'elegibility', input.ID, this.trace
+ .elegibility[input.ID]);
+ if (this.selfconnected()) {
+ if (this.selfconnection.gater) {
+ if (input.gater)
+ buildSentence(trace, ' = ', self_gain, ' * ', self_weight,
+ ' * ', trace, ' + ', input_gain, ' * ', input_activation,
+ store_trace);
+ else
+ buildSentence(trace, ' = ', self_gain, ' * ', self_weight,
+ ' * ', trace, ' + ', input_activation, store_trace);
+ } else {
+ if (input.gater)
+ buildSentence(trace, ' = ', self_weight, ' * ', trace, ' + ',
+ input_gain, ' * ', input_activation, store_trace);
+ else
+ buildSentence(trace, ' = ', self_weight, ' * ', trace, ' + ',
+ input_activation, store_trace);
+ }
+ } else {
+ if (input.gater)
+ buildSentence(trace, ' = ', input_gain, ' * ', input_activation,
+ store_trace);
+ else
+ buildSentence(trace, ' = ', input_activation, store_trace);
+ }
+ for (var id in this.trace.extended) {
+ // extended elegibility trace
+ var neuron = this.neighboors[id];
+ var influence = getVar('influences[' + neuron.ID + ']');
+
+ var trace = getVar(this, 'trace', 'elegibility', input.ID, this.trace
+ .elegibility[input.ID]);
+ var xtrace = getVar(this, 'trace', 'extended', neuron.ID, input.ID,
+ this.trace.extended[neuron.ID][input.ID]);
+ if (neuron.selfconnected())
+ var neuron_self_weight = getVar(neuron.selfconnection, 'weight');
+ if (neuron.selfconnection.gater)
+ var neuron_self_gain = getVar(neuron.selfconnection, 'gain');
+ if (neuron.selfconnected())
+ if (neuron.selfconnection.gater)
+ buildSentence(xtrace, ' = ', neuron_self_gain, ' * ',
+ neuron_self_weight, ' * ', xtrace, ' + ', derivative, ' * ',
+ trace, ' * ', influence, store_trace);
+ else
+ buildSentence(xtrace, ' = ', neuron_self_weight, ' * ',
+ xtrace, ' + ', derivative, ' * ', trace, ' * ',
+ influence, store_trace);
+ else
+ buildSentence(xtrace, ' = ', derivative, ' * ', trace, ' * ',
+ influence, store_trace);
+ }
+ }
+ for (var connection in this.connections.gated) {
+ var gated_gain = getVar(this.connections.gated[connection], 'gain');
+ buildSentence(gated_gain, ' = ', activation, store_activation);
+ }
+ }
+ if (!isInput) {
+ var responsibility = getVar(this, 'error', 'responsibility', this.error
+ .responsibility);
+ if (isOutput) {
+ var target = getVar('target');
+ buildSentence(responsibility, ' = ', target, ' - ', activation,
+ store_propagation);
+ for (var id in this.connections.inputs) {
+ var input = this.connections.inputs[id];
+ var trace = getVar(this, 'trace', 'elegibility', input.ID, this.trace
+ .elegibility[input.ID]);
+ var input_weight = getVar(input, 'weight');
+ buildSentence(input_weight, ' += ', rate, ' * (', responsibility,
+ ' * ', trace, ')', store_propagation);
+ }
+ outputs.push(activation.id);
+ } else {
+ if (!noProjections && !noGates) {
+ var error = getVar('aux');
+ for (var id in this.connections.projected) {
+ var connection = this.connections.projected[id];
+ var neuron = connection.to;
+ var connection_weight = getVar(connection, 'weight');
+ var neuron_responsibility = getVar(neuron, 'error',
+ 'responsibility', neuron.error.responsibility);
+ if (connection.gater) {
+ var connection_gain = getVar(connection, 'gain');
+ buildSentence(error, ' += ', neuron_responsibility, ' * ',
+ connection_gain, ' * ', connection_weight,
+ store_propagation);
+ } else
+ buildSentence(error, ' += ', neuron_responsibility, ' * ',
+ connection_weight, store_propagation);
+ }
+ var projected = getVar(this, 'error', 'projected', this.error.projected);
+ buildSentence(projected, ' = ', derivative, ' * ', error,
+ store_propagation);
+ buildSentence(error, ' = 0', store_propagation);
+ for (var id in this.trace.extended) {
+ var neuron = this.neighboors[id];
+ var influence = getVar('aux_2');
+ var neuron_old = getVar(neuron, 'old');
+ if (neuron.selfconnection.gater == this)
+ buildSentence(influence, ' = ', neuron_old, store_propagation);
+ else
+ buildSentence(influence, ' = 0', store_propagation);
+ for (var input in this.trace.influences[neuron.ID]) {
+ var connection = this.trace.influences[neuron.ID][input];
+ var connection_weight = getVar(connection, 'weight');
+ var neuron_activation = getVar(connection.from, 'activation');
+ buildSentence(influence, ' += ', connection_weight, ' * ',
+ neuron_activation, store_propagation);
+ }
+ var neuron_responsibility = getVar(neuron, 'error',
+ 'responsibility', neuron.error.responsibility);
+ buildSentence(error, ' += ', neuron_responsibility, ' * ',
+ influence, store_propagation);
+ }
+ var gated = getVar(this, 'error', 'gated', this.error.gated);
+ buildSentence(gated, ' = ', derivative, ' * ', error,
+ store_propagation);
+ buildSentence(responsibility, ' = ', projected, ' + ', gated,
+ store_propagation);
+ for (var id in this.connections.inputs) {
+ var input = this.connections.inputs[id];
+ var gradient = getVar('aux');
+ var trace = getVar(this, 'trace', 'elegibility', input.ID, this
+ .trace.elegibility[input.ID]);
+ buildSentence(gradient, ' = ', projected, ' * ', trace,
+ store_propagation);
+ for (var id in this.trace.extended) {
+ var neuron = this.neighboors[id];
+ var neuron_responsibility = getVar(neuron, 'error',
+ 'responsibility', neuron.error.responsibility);
+ var xtrace = getVar(this, 'trace', 'extended', neuron.ID,
+ input.ID, this.trace.extended[neuron.ID][input.ID]);
+ buildSentence(gradient, ' += ', neuron_responsibility, ' * ',
+ xtrace, store_propagation);
+ }
+ var input_weight = getVar(input, 'weight');
+ buildSentence(input_weight, ' += ', rate, ' * ', gradient,
+ store_propagation);
+ }
+
+ } else if (noGates) {
+ buildSentence(responsibility, ' = 0', store_propagation);
+ for (var id in this.connections.projected) {
+ var connection = this.connections.projected[id];
+ var neuron = connection.to;
+ var connection_weight = getVar(connection, 'weight');
+ var neuron_responsibility = getVar(neuron, 'error',
+ 'responsibility', neuron.error.responsibility);
+ if (connection.gater) {
+ var connection_gain = getVar(connection, 'gain');
+ buildSentence(responsibility, ' += ', neuron_responsibility,
+ ' * ', connection_gain, ' * ', connection_weight,
+ store_propagation);
+ } else
+ buildSentence(responsibility, ' += ', neuron_responsibility,
+ ' * ', connection_weight, store_propagation);
+ }
+ buildSentence(responsibility, ' *= ', derivative,
+ store_propagation);
+ for (var id in this.connections.inputs) {
+ var input = this.connections.inputs[id];
+ var trace = getVar(this, 'trace', 'elegibility', input.ID, this
+ .trace.elegibility[input.ID]);
+ var input_weight = getVar(input, 'weight');
+ buildSentence(input_weight, ' += ', rate, ' * (',
+ responsibility, ' * ', trace, ')', store_propagation);
+ }
+ } else if (noProjections) {
+ buildSentence(responsibility, ' = 0', store_propagation);
+ for (var id in this.trace.extended) {
+ var neuron = this.neighboors[id];
+ var influence = getVar('aux');
+ var neuron_old = getVar(neuron, 'old');
+ if (neuron.selfconnection.gater == this)
+ buildSentence(influence, ' = ', neuron_old, store_propagation);
+ else
+ buildSentence(influence, ' = 0', store_propagation);
+ for (var input in this.trace.influences[neuron.ID]) {
+ var connection = this.trace.influences[neuron.ID][input];
+ var connection_weight = getVar(connection, 'weight');
+ var neuron_activation = getVar(connection.from, 'activation');
+ buildSentence(influence, ' += ', connection_weight, ' * ',
+ neuron_activation, store_propagation);
+ }
+ var neuron_responsibility = getVar(neuron, 'error',
+ 'responsibility', neuron.error.responsibility);
+ buildSentence(responsibility, ' += ', neuron_responsibility,
+ ' * ', influence, store_propagation);
+ }
+ buildSentence(responsibility, ' *= ', derivative,
+ store_propagation);
+ for (var id in this.connections.inputs) {
+ var input = this.connections.inputs[id];
+ var gradient = getVar('aux');
+ buildSentence(gradient, ' = 0', store_propagation);
+ for (var id in this.trace.extended) {
+ var neuron = this.neighboors[id];
+ var neuron_responsibility = getVar(neuron, 'error',
+ 'responsibility', neuron.error.responsibility);
+ var xtrace = getVar(this, 'trace', 'extended', neuron.ID,
+ input.ID, this.trace.extended[neuron.ID][input.ID]);
+ buildSentence(gradient, ' += ', neuron_responsibility, ' * ',
+ xtrace, store_propagation);
+ }
+ var input_weight = getVar(input, 'weight');
+ buildSentence(input_weight, ' += ', rate, ' * ', gradient,
+ store_propagation);
+ }
+ }
+ }
+ buildSentence(bias, ' += ', rate, ' * ', responsibility,
+ store_propagation);
+ }
+ return {
+ memory: varID,
+ neurons: neurons + 1,
+ inputs: inputs,
+ outputs: outputs,
+ targets: targets,
+ variables: variables,
+ activation_sentences: activation_sentences,
+ trace_sentences: trace_sentences,
+ propagation_sentences: propagation_sentences,
+ layers: layers
+ }
+ }
+ }
+
+
+ // represents a connection between two neurons
+ Neuron.connection = function Connection(from, to, weight) {
+
+ if (!from || !to)
+ throw new Error("Connection Error: Invalid neurons");
+
+ this.ID = Neuron.connection.uid();
+ this.from = from;
+ this.to = to;
+ this.weight = typeof weight == 'undefined' ? Math.random() * .2 - .1 :
+ weight;
+ this.gain = 1;
+ this.gater = null;
+ }
+
+
+ // squashing functions
+ Neuron.squash = {};
+
+ // eq. 5 & 5'
+ Neuron.squash.LOGISTIC = function(x, derivate) {
+ if (!derivate)
+ return 1 / (1 + Math.exp(-x));
+ var fx = Neuron.squash.LOGISTIC(x);
+ return fx * (1 - fx);
+ };
+ Neuron.squash.TANH = function(x, derivate) {
+ if (derivate)
+ return 1 - Math.pow(Neuron.squash.TANH(x), 2);
+ var eP = Math.exp(x);
+ var eN = 1 / eP;
+ return (eP - eN) / (eP + eN);
+ };
+ Neuron.squash.IDENTITY = function(x, derivate) {
+ return derivate ? 1 : x;
+ };
+ Neuron.squash.HLIM = function(x, derivate) {
+ return derivate ? 1 : x > 0 ? 1 : 0;
+ };
+ Neuron.squash.RELU = function(x, derivate) {
+ if (derivate)
+ return x > 0 ? 1 : 0;
+ return x > 0 ? x : 0;
+ };
+
+ // unique ID's
+ (function() {
+ var neurons = 0;
+ var connections = 0;
+ Neuron.uid = function() {
+ return neurons++;
+ }
+ Neuron.connection.uid = function() {
+ return connections++;
+ }
+ Neuron.quantity = function() {
+ return {
+ neurons: neurons,
+ connections: connections
+ }
+ }
+ })();
+
+ /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(2)(module)))
+
+/***/ },
+/* 2 */
+/***/ function(module, exports) {
+
+ module.exports = function(module) {
+ if(!module.webpackPolyfill) {
+ module.deprecate = function() {};
+ module.paths = [];
+ // module.parent = undefined by default
+ module.children = [];
+ module.webpackPolyfill = 1;
+ }
+ return module;
+ }
+
+
+/***/ },
+/* 3 */
+/***/ function(module, exports, __webpack_require__) {
+
+ /* WEBPACK VAR INJECTION */(function(module) {// export
+ if (module) module.exports = Layer;
+
+ // import
+ var Neuron = __webpack_require__(1)
+ , Network = __webpack_require__(4)
+
+ /*******************************************************************************************
+ LAYER
+ *******************************************************************************************/
+
+ function Layer(size, label) {
+ this.size = size | 0;
+ this.list = [];
+ this.label = label || null;
+ this.connectedTo = [];
+
+ while (size--) {
+ var neuron = new Neuron();
+ this.list.push(neuron);
+ }
+ }
+
+ Layer.prototype = {
+
+ // activates all the neurons in the layer
+ activate: function(input) {
+
+ var activations = [];
+
+ if (typeof input != 'undefined') {
+ if (input.length != this.size)
+ throw new Error("INPUT size and LAYER size must be the same to activate!");
+
+ for (var id in this.list) {
+ var neuron = this.list[id];
+ var activation = neuron.activate(input[id]);
+ activations.push(activation);
+ }
+ } else {
+ for (var id in this.list) {
+ var neuron = this.list[id];
+ var activation = neuron.activate();
+ activations.push(activation);
+ }
+ }
+ return activations;
+ },
+
+ // propagates the error on all the neurons of the layer
+ propagate: function(rate, target) {
+
+ if (typeof target != 'undefined') {
+ if (target.length != this.size)
+ throw new Error("TARGET size and LAYER size must be the same to propagate!");
+
+ for (var id = this.list.length - 1; id >= 0; id--) {
+ var neuron = this.list[id];
+ neuron.propagate(rate, target[id]);
+ }
+ } else {
+ for (var id = this.list.length - 1; id >= 0; id--) {
+ var neuron = this.list[id];
+ neuron.propagate(rate);
+ }
+ }
+ },
+
+ // projects a connection from this layer to another one
+ project: function(layer, type, weights) {
+
+ if (layer instanceof Network)
+ layer = layer.layers.input;
+
+ if (layer instanceof Layer) {
+ if (!this.connected(layer))
+ return new Layer.connection(this, layer, type, weights);
+ } else
+ throw new Error("Invalid argument, you can only project connections to LAYERS and NETWORKS!");
+
+
+ },
+
+ // gates a connection betwenn two layers
+ gate: function(connection, type) {
+
+ if (type == Layer.gateType.INPUT) {
+ if (connection.to.size != this.size)
+ throw new Error("GATER layer and CONNECTION.TO layer must be the same size in order to gate!");
+
+ for (var id in connection.to.list) {
+ var neuron = connection.to.list[id];
+ var gater = this.list[id];
+ for (var input in neuron.connections.inputs) {
+ var gated = neuron.connections.inputs[input];
+ if (gated.ID in connection.connections)
+ gater.gate(gated);
+ }
+ }
+ } else if (type == Layer.gateType.OUTPUT) {
+ if (connection.from.size != this.size)
+ throw new Error("GATER layer and CONNECTION.FROM layer must be the same size in order to gate!");
+
+ for (var id in connection.from.list) {
+ var neuron = connection.from.list[id];
+ var gater = this.list[id];
+ for (var projected in neuron.connections.projected) {
+ var gated = neuron.connections.projected[projected];
+ if (gated.ID in connection.connections)
+ gater.gate(gated);
+ }
+ }
+ } else if (type == Layer.gateType.ONE_TO_ONE) {
+ if (connection.size != this.size)
+ throw new Error("The number of GATER UNITS must be the same as the number of CONNECTIONS to gate!");
+
+ for (var id in connection.list) {
+ var gater = this.list[id];
+ var gated = connection.list[id];
+ gater.gate(gated);
+ }
+ }
+ connection.gatedfrom.push({layer: this, type: type});
+ },
+
+ // true or false whether the whole layer is self-connected or not
+ selfconnected: function() {
+
+ for (var id in this.list) {
+ var neuron = this.list[id];
+ if (!neuron.selfconnected())
+ return false;
+ }
+ return true;
+ },
+
+ // true of false whether the layer is connected to another layer (parameter) or not
+ connected: function(layer) {
+ // Check if ALL to ALL connection
+ var connections = 0;
+ for (var here in this.list) {
+ for (var there in layer.list) {
+ var from = this.list[here];
+ var to = layer.list[there];
+ var connected = from.connected(to);
+ if (connected.type == 'projected')
+ connections++;
+ }
+ }
+ if (connections == this.size * layer.size)
+ return Layer.connectionType.ALL_TO_ALL;
+
+ // Check if ONE to ONE connection
+ connections = 0;
+ for (var neuron in this.list) {
+ var from = this.list[neuron];
+ var to = layer.list[neuron];
+ var connected = from.connected(to);
+ if (connected.type == 'projected')
+ connections++;
+ }
+ if (connections == this.size)
+ return Layer.connectionType.ONE_TO_ONE;
+ },
+
+ // clears all the neuorns in the layer
+ clear: function() {
+ for (var id in this.list) {
+ var neuron = this.list[id];
+ neuron.clear();
+ }
+ },
+
+ // resets all the neurons in the layer
+ reset: function() {
+ for (var id in this.list) {
+ var neuron = this.list[id];
+ neuron.reset();
+ }
+ },
+
+ // returns all the neurons in the layer (array)
+ neurons: function() {
+ return this.list;
+ },
+
+ // adds a neuron to the layer
+ add: function(neuron) {
+ this.neurons[neuron.ID] = neuron || new Neuron();
+ this.list.push(neuron);
+ this.size++;
+ },
+
+ set: function(options) {
+ options = options || {};
+
+ for (var i in this.list) {
+ var neuron = this.list[i];
+ if (options.label)
+ neuron.label = options.label + '_' + neuron.ID;
+ if (options.squash)
+ neuron.squash = options.squash;
+ if (options.bias)
+ neuron.bias = options.bias;
+ }
+ return this;
+ }
+ }
+
+ // represents a connection from one layer to another, and keeps track of its weight and gain
+ Layer.connection = function LayerConnection(fromLayer, toLayer, type, weights) {
+ this.ID = Layer.connection.uid();
+ this.from = fromLayer;
+ this.to = toLayer;
+ this.selfconnection = toLayer == fromLayer;
+ this.type = type;
+ this.connections = {};
+ this.list = [];
+ this.size = 0;
+ this.gatedfrom = [];
+
+ if (typeof this.type == 'undefined')
+ {
+ if (fromLayer == toLayer)
+ this.type = Layer.connectionType.ONE_TO_ONE;
+ else
+ this.type = Layer.connectionType.ALL_TO_ALL;
+ }
+
+ if (this.type == Layer.connectionType.ALL_TO_ALL ||
+ this.type == Layer.connectionType.ALL_TO_ELSE) {
+ for (var here in this.from.list) {
+ for (var there in this.to.list) {
+ var from = this.from.list[here];
+ var to = this.to.list[there];
+ if(this.type == Layer.connectionType.ALL_TO_ELSE && from == to)
+ continue;
+ var connection = from.project(to, weights);
+
+ this.connections[connection.ID] = connection;
+ this.size = this.list.push(connection);
+ }
+ }
+ } else if (this.type == Layer.connectionType.ONE_TO_ONE) {
+
+ for (var neuron in this.from.list) {
+ var from = this.from.list[neuron];
+ var to = this.to.list[neuron];
+ var connection = from.project(to, weights);
+
+ this.connections[connection.ID] = connection;
+ this.size = this.list.push(connection);
+ }
+ }
+
+ fromLayer.connectedTo.push(this);
+ }
+
+ // types of connections
+ Layer.connectionType = {};
+ Layer.connectionType.ALL_TO_ALL = "ALL TO ALL";
+ Layer.connectionType.ONE_TO_ONE = "ONE TO ONE";
+ Layer.connectionType.ALL_TO_ELSE = "ALL TO ELSE";
+
+ // types of gates
+ Layer.gateType = {};
+ Layer.gateType.INPUT = "INPUT";
+ Layer.gateType.OUTPUT = "OUTPUT";
+ Layer.gateType.ONE_TO_ONE = "ONE TO ONE";
+
+ (function() {
+ var connections = 0;
+ Layer.connection.uid = function() {
+ return connections++;
+ }
+ })();
+
+ /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(2)(module)))
+
+/***/ },
+/* 4 */
+/***/ function(module, exports, __webpack_require__) {
+
+ /* WEBPACK VAR INJECTION */(function(module) {// export
+ if (module) module.exports = Network;
+
+ // import
+ var Neuron = __webpack_require__(1)
+ , Layer = __webpack_require__(3)
+ , Trainer = __webpack_require__(5)
+
+ /*******************************************************************************************
+ NETWORK
+ *******************************************************************************************/
+
+ function Network(layers) {
+ if (typeof layers != 'undefined') {
+ this.layers = layers || {
+ input: null,
+ hidden: {},
+ output: null
+ };
+ this.optimized = null;
+ }
+ }
+ Network.prototype = {
+
+ // feed-forward activation of all the layers to produce an ouput
+ activate: function(input) {
+
+ if (this.optimized === false)
+ {
+ this.layers.input.activate(input);
+ for (var layer in this.layers.hidden)
+ this.layers.hidden[layer].activate();
+ return this.layers.output.activate();
+ }
+ else
+ {
+ if (this.optimized == null)
+ this.optimize();
+ return this.optimized.activate(input);
+ }
+ },
+
+ // back-propagate the error thru the network
+ propagate: function(rate, target) {
+
+ if (this.optimized === false)
+ {
+ this.layers.output.propagate(rate, target);
+ var reverse = [];
+ for (var layer in this.layers.hidden)
+ reverse.push(this.layers.hidden[layer]);
+ reverse.reverse();
+ for (var layer in reverse)
+ reverse[layer].propagate(rate);
+ }
+ else
+ {
+ if (this.optimized == null)
+ this.optimize();
+ this.optimized.propagate(rate, target);
+ }
+ },
+
+ // project a connection to another unit (either a network or a layer)
+ project: function(unit, type, weights) {
+
+ if (this.optimized)
+ this.optimized.reset();
+
+ if (unit instanceof Network)
+ return this.layers.output.project(unit.layers.input, type, weights);
+
+ if (unit instanceof Layer)
+ return this.layers.output.project(unit, type, weights);
+
+ throw new Error("Invalid argument, you can only project connections to LAYERS and NETWORKS!");
+ },
+
+ // let this network gate a connection
+ gate: function(connection, type) {
+ if (this.optimized)
+ this.optimized.reset();
+ this.layers.output.gate(connection, type);
+ },
+
+ // clear all elegibility traces and extended elegibility traces (the network forgets its context, but not what was trained)
+ clear: function() {
+
+ this.restore();
+
+ var inputLayer = this.layers.input,
+ outputLayer = this.layers.output;
+
+ inputLayer.clear();
+ for (var layer in this.layers.hidden) {
+ var hiddenLayer = this.layers.hidden[layer];
+ hiddenLayer.clear();
+ }
+ outputLayer.clear();
+
+ if (this.optimized)
+ this.optimized.reset();
+ },
+
+ // reset all weights and clear all traces (ends up like a new network)
+ reset: function() {
+
+ this.restore();
+
+ var inputLayer = this.layers.input,
+ outputLayer = this.layers.output;
+
+ inputLayer.reset();
+ for (var layer in this.layers.hidden) {
+ var hiddenLayer = this.layers.hidden[layer];
+ hiddenLayer.reset();
+ }
+ outputLayer.reset();
+
+ if (this.optimized)
+ this.optimized.reset();
+ },
+
+ // hardcodes the behaviour of the whole network into a single optimized function
+ optimize: function() {
+
+ var that = this;
+ var optimized = {};
+ var neurons = this.neurons();
+
+ for (var i in neurons) {
+ var neuron = neurons[i].neuron;
+ var layer = neurons[i].layer;
+ while (neuron.neuron)
+ neuron = neuron.neuron;
+ optimized = neuron.optimize(optimized, layer);
+ }
+ for (var i in optimized.propagation_sentences)
+ optimized.propagation_sentences[i].reverse();
+ optimized.propagation_sentences.reverse();
+
+ var hardcode = "";
+ hardcode += "var F = Float64Array ? new Float64Array(" + optimized.memory +
+ ") : []; ";
+ for (var i in optimized.variables)
+ hardcode += "F[" + optimized.variables[i].id + "] = " + (optimized.variables[
+ i].value || 0) + "; ";
+ hardcode += "var activate = function(input){\n";
+ for (var i in optimized.inputs)
+ hardcode += "F[" + optimized.inputs[i] + "] = input[" + i + "]; ";
+ for (var currentLayer in optimized.activation_sentences) {
+ if (optimized.activation_sentences[currentLayer].length > 0) {
+ for (var currentNeuron in optimized.activation_sentences[currentLayer]) {
+ hardcode += optimized.activation_sentences[currentLayer][currentNeuron].join(" ");
+ hardcode += optimized.trace_sentences[currentLayer][currentNeuron].join(" ");
+ }
+ }
+ }
+ hardcode += " var output = []; "
+ for (var i in optimized.outputs)
+ hardcode += "output[" + i + "] = F[" + optimized.outputs[i] + "]; ";
+ hardcode += "return output; }; "
+ hardcode += "var propagate = function(rate, target){\n";
+ hardcode += "F[" + optimized.variables.rate.id + "] = rate; ";
+ for (var i in optimized.targets)
+ hardcode += "F[" + optimized.targets[i] + "] = target[" + i + "]; ";
+ for (var currentLayer in optimized.propagation_sentences)
+ for (var currentNeuron in optimized.propagation_sentences[currentLayer])
+ hardcode += optimized.propagation_sentences[currentLayer][currentNeuron].join(" ") + " ";
+ hardcode += " };\n";
+ hardcode +=
+ "var ownership = function(memoryBuffer){\nF = memoryBuffer;\nthis.memory = F;\n};\n";
+ hardcode +=
+ "return {\nmemory: F,\nactivate: activate,\npropagate: propagate,\nownership: ownership\n};";
+ hardcode = hardcode.split(";").join(";\n");
+
+ var constructor = new Function(hardcode);
+
+ var network = constructor();
+ network.data = {
+ variables: optimized.variables,
+ activate: optimized.activation_sentences,
+ propagate: optimized.propagation_sentences,
+ trace: optimized.trace_sentences,
+ inputs: optimized.inputs,
+ outputs: optimized.outputs,
+ check_activation: this.activate,
+ check_propagation: this.propagate
+ }
+
+ network.reset = function() {
+ if (that.optimized) {
+ that.optimized = null;
+ that.activate = network.data.check_activation;
+ that.propagate = network.data.check_propagation;
+ }
+ }
+
+ this.optimized = network;
+ this.activate = network.activate;
+ this.propagate = network.propagate;
+ },
+
+ // restores all the values from the optimized network the their respective objects in order to manipulate the network
+ restore: function() {
+ if (!this.optimized)
+ return;
+
+ var optimized = this.optimized;
+
+ var getValue = function() {
+ var args = Array.prototype.slice.call(arguments);
+
+ var unit = args.shift();
+ var prop = args.pop();
+
+ var id = prop + '_';
+ for (var property in args)
+ id += args[property] + '_';
+ id += unit.ID;
+
+ var memory = optimized.memory;
+ var variables = optimized.data.variables;
+
+ if (id in variables)
+ return memory[variables[id].id];
+ return 0;
+ }
+
+ var list = this.neurons();
+
+ // link id's to positions in the array
+ var ids = {};
+ for (var i in list) {
+ var neuron = list[i].neuron;
+ while (neuron.neuron)
+ neuron = neuron.neuron;
+
+ neuron.state = getValue(neuron, 'state');
+ neuron.old = getValue(neuron, 'old');
+ neuron.activation = getValue(neuron, 'activation');
+ neuron.bias = getValue(neuron, 'bias');
+
+ for (var input in neuron.trace.elegibility)
+ neuron.trace.elegibility[input] = getValue(neuron, 'trace',
+ 'elegibility', input);
+
+ for (var gated in neuron.trace.extended)
+ for (var input in neuron.trace.extended[gated])
+ neuron.trace.extended[gated][input] = getValue(neuron, 'trace',
+ 'extended', gated, input);
+ }
+
+ // get connections
+ for (var i in list) {
+ var neuron = list[i].neuron;
+ while (neuron.neuron)
+ neuron = neuron.neuron;
+
+ for (var j in neuron.connections.projected) {
+ var connection = neuron.connections.projected[j];
+ connection.weight = getValue(connection, 'weight');
+ connection.gain = getValue(connection, 'gain');
+ }
+ }
+ },
+
+ // returns all the neurons in the network
+ neurons: function() {
+
+ var neurons = [];
+
+ var inputLayer = this.layers.input.neurons(),
+ outputLayer = this.layers.output.neurons();
+
+ for (var neuron in inputLayer)
+ neurons.push({
+ neuron: inputLayer[neuron],
+ layer: 'input'
+ });
+
+ for (var layer in this.layers.hidden) {
+ var hiddenLayer = this.layers.hidden[layer].neurons();
+ for (var neuron in hiddenLayer)
+ neurons.push({
+ neuron: hiddenLayer[neuron],
+ layer: layer
+ });
+ }
+ for (var neuron in outputLayer)
+ neurons.push({
+ neuron: outputLayer[neuron],
+ layer: 'output'
+ });
+
+ return neurons;
+ },
+
+ // returns number of inputs of the network
+ inputs: function() {
+ return this.layers.input.size;
+ },
+
+ // returns number of outputs of hte network
+ outputs: function() {
+ return this.layers.output.size;
+ },
+
+ // sets the layers of the network
+ set: function(layers) {
+
+ this.layers = layers;
+ if (this.optimized)
+ this.optimized.reset();
+ },
+
+ setOptimize: function(bool){
+ this.restore();
+ if (this.optimized)
+ this.optimized.reset();
+ this.optimized = bool? null : false;
+ },
+
+ // returns a json that represents all the neurons and connections of the network
+ toJSON: function(ignoreTraces) {
+
+ this.restore();
+
+ var list = this.neurons();
+ var neurons = [];
+ var connections = [];
+
+ // link id's to positions in the array
+ var ids = {};
+ for (var i in list) {
+ var neuron = list[i].neuron;
+ while (neuron.neuron)
+ neuron = neuron.neuron;
+ ids[neuron.ID] = i;
+
+ var copy = {
+ trace: {
+ elegibility: {},
+ extended: {}
+ },
+ state: neuron.state,
+ old: neuron.old,
+ activation: neuron.activation,
+ bias: neuron.bias,
+ layer: list[i].layer
+ };
+
+ copy.squash = neuron.squash == Neuron.squash.LOGISTIC ? "LOGISTIC" :
+ neuron.squash == Neuron.squash.TANH ? "TANH" :
+ neuron.squash == Neuron.squash.IDENTITY ? "IDENTITY" :
+ neuron.squash == Neuron.squash.HLIM ? "HLIM" :
+ null;
+
+ neurons.push(copy);
+ }
+
+ // get connections
+ for (var i in list) {
+ var neuron = list[i].neuron;
+ while (neuron.neuron)
+ neuron = neuron.neuron;
+
+ for (var j in neuron.connections.projected) {
+ var connection = neuron.connections.projected[j];
+ connections.push({
+ from: ids[connection.from.ID],
+ to: ids[connection.to.ID],
+ weight: connection.weight,
+ gater: connection.gater ? ids[connection.gater.ID] : null,
+ });
+ }
+ if (neuron.selfconnected())
+ connections.push({
+ from: ids[neuron.ID],
+ to: ids[neuron.ID],
+ weight: neuron.selfconnection.weight,
+ gater: neuron.selfconnection.gater ? ids[neuron.selfconnection.gater.ID] : null,
+ });
+ }
+
+ return {
+ neurons: neurons,
+ connections: connections
+ }
+ },
+
+ // export the topology into dot language which can be visualized as graphs using dot
+ /* example: ... console.log(net.toDotLang());
+ $ node example.js > example.dot
+ $ dot example.dot -Tpng > out.png
+ */
+ toDot: function(edgeConnection) {
+ if (! typeof edgeConnection)
+ edgeConnection = false;
+ var code = "digraph nn {\n rankdir = BT\n";
+ var layers = [this.layers.input].concat(this.layers.hidden, this.layers.output);
+ for (var layer in layers) {
+ for (var to in layers[layer].connectedTo) { // projections
+ var connection = layers[layer].connectedTo[to];
+ var layerTo = connection.to;
+ var size = connection.size;
+ var layerID = layers.indexOf(layers[layer]);
+ var layerToID = layers.indexOf(layerTo);
+ /* http://stackoverflow.com/questions/26845540/connect-edges-with-graph-dot
+ * DOT does not support edge-to-edge connections
+ * This workaround produces somewhat weird graphs ...
+ */
+ if ( edgeConnection) {
+ if (connection.gatedfrom.length) {
+ var fakeNode = "fake" + layerID + "_" + layerToID;
+ code += " " + fakeNode +
+ " [label = \"\", shape = point, width = 0.01, height = 0.01]\n";
+ code += " " + layerID + " -> " + fakeNode + " [label = " + size + ", arrowhead = none]\n";
+ code += " " + fakeNode + " -> " + layerToID + "\n";
+ } else
+ code += " " + layerID + " -> " + layerToID + " [label = " + size + "]\n";
+ for (var from in connection.gatedfrom) { // gatings
+ var layerfrom = connection.gatedfrom[from].layer;
+ var layerfromID = layers.indexOf(layerfrom);
+ code += " " + layerfromID + " -> " + fakeNode + " [color = blue]\n";
+ }
+ } else {
+ code += " " + layerID + " -> " + layerToID + " [label = " + size + "]\n";
+ for (var from in connection.gatedfrom) { // gatings
+ var layerfrom = connection.gatedfrom[from].layer;
+ var layerfromID = layers.indexOf(layerfrom);
+ code += " " + layerfromID + " -> " + layerToID + " [color = blue]\n";
+ }
+ }
+ }
+ }
+ code += "}\n";
+ return {
+ code: code,
+ link: "https://chart.googleapis.com/chart?chl=" + escape(code.replace("/ /g", "+")) + "&cht=gv"
+ }
+ },
+
+ // returns a function that works as the activation of the network and can be used without depending on the library
+ standalone: function() {
+ if (!this.optimized)
+ this.optimize();
+
+ var data = this.optimized.data;
+
+ // build activation function
+ var activation = "function (input) {\n";
+
+ // build inputs
+ for (var i in data.inputs)
+ activation += "F[" + data.inputs[i] + "] = input[" + i + "];\n";
+
+ // build network activation
+ for (var neuron in data.activate) { // shouldn't this be layer?
+ for (var sentence in data.activate[neuron])
+ activation += data.activate[neuron][sentence].join('') + "\n";
+ }
+
+ // build outputs
+ activation += "var output = [];\n";
+ for (var i in data.outputs)
+ activation += "output[" + i + "] = F[" + data.outputs[i] + "];\n";
+ activation += "return output;\n}";
+
+ // reference all the positions in memory
+ var memory = activation.match(/F\[(\d+)\]/g);
+ var dimension = 0;
+ var ids = {};
+ for (var address in memory) {
+ var tmp = memory[address].match(/\d+/)[0];
+ if (!(tmp in ids)) {
+ ids[tmp] = dimension++;
+ }
+ }
+ var hardcode = "F = {\n";
+ for (var i in ids)
+ hardcode += ids[i] + ": " + this.optimized.memory[i] + ",\n";
+ hardcode = hardcode.substring(0, hardcode.length - 2) + "\n};\n";
+ hardcode = "var run = " + activation.replace(/F\[(\d+)]/g, function(
+ index) {
+ return 'F[' + ids[index.match(/\d+/)[0]] + ']'
+ }).replace("{\n", "{\n" + hardcode + "") + ";\n";
+ hardcode += "return run";
+
+ // return standalone function
+ return new Function(hardcode)();
+ },
+
+
+ // Return a HTML5 WebWorker specialized on training the network stored in `memory`.
+ // Train based on the given dataSet and options.
+ // The worker returns the updated `memory` when done.
+ worker: function(memory, set, options) {
+
+ // Copy the options and set defaults (options might be different for each worker)
+ var workerOptions = {};
+ if(options) workerOptions = options;
+ workerOptions.rate = options.rate || .2;
+ workerOptions.iterations = options.iterations || 100000;
+ workerOptions.error = options.error || .005;
+ workerOptions.cost = options.cost || null;
+ workerOptions.crossValidate = options.crossValidate || null;
+
+ // Cost function might be different for each worker
+ costFunction = "var cost = " + (options && options.cost || this.cost || Trainer.cost.MSE) + ";\n";
+ var workerFunction = Network.getWorkerSharedFunctions();
+ workerFunction = workerFunction.replace(/var cost = options && options\.cost \|\| this\.cost \|\| Trainer\.cost\.MSE;/g, costFunction);
+
+ // Set what we do when training is finished
+ workerFunction = workerFunction.replace('return results;',
+ 'postMessage({action: "done", message: results, memoryBuffer: F}, [F.buffer]);');
+
+ // Replace log with postmessage
+ workerFunction = workerFunction.replace("console.log('iterations', iterations, 'error', error, 'rate', currentRate)",
+ "postMessage({action: 'log', message: {\n" +
+ "iterations: iterations,\n" +
+ "error: error,\n" +
+ "rate: currentRate\n" +
+ "}\n" +
+ "})");
+
+ // Replace schedule with postmessage
+ workerFunction = workerFunction.replace("abort = this.schedule.do({ error: error, iterations: iterations, rate: currentRate })",
+ "postMessage({action: 'schedule', message: {\n" +
+ "iterations: iterations,\n" +
+ "error: error,\n" +
+ "rate: currentRate\n" +
+ "}\n" +
+ "})");
+
+ if (!this.optimized)
+ this.optimize();
+
+ var hardcode = "var inputs = " + this.optimized.data.inputs.length + ";\n";
+ hardcode += "var outputs = " + this.optimized.data.outputs.length + ";\n";
+ hardcode += "var F = new Float64Array([" + this.optimized.memory.toString() + "]);\n";
+ hardcode += "var activate = " + this.optimized.activate.toString() + ";\n";
+ hardcode += "var propagate = " + this.optimized.propagate.toString() + ";\n";
+ hardcode +=
+ "onmessage = function(e) {\n" +
+ "if (e.data.action == 'startTraining') {\n" +
+ "train(" + JSON.stringify(set) + "," + JSON.stringify(workerOptions) + ");\n" +
+ "}\n" +
+ "}";
+
+ var workerSourceCode = workerFunction + '\n' + hardcode;
+ var blob = new Blob([workerSourceCode]);
+ var blobURL = window.URL.createObjectURL(blob);
+
+ return new Worker(blobURL);
+ },
+
+ // returns a copy of the network
+ clone: function() {
+ return Network.fromJSON(this.toJSON());
+ }
+ };
+
+ /**
+ * Creates a static String to store the source code of the functions
+ * that are identical for all the workers (train, _trainSet, test)
+ *
+ * @return {String} Source code that can train a network inside a worker.
+ * @static
+ */
+ Network.getWorkerSharedFunctions = function() {
+ // If we already computed the source code for the shared functions
+ if(typeof Network._SHARED_WORKER_FUNCTIONS !== 'undefined')
+ return Network._SHARED_WORKER_FUNCTIONS;
+
+ // Otherwise compute and return the source code
+ // We compute them by simply copying the source code of the train, _trainSet and test functions
+ // using the .toString() method
+
+ // Load and name the train function
+ var train_f = Trainer.prototype.train.toString();
+ train_f = train_f.replace('function (set', 'function train(set') + '\n';
+
+ // Load and name the _trainSet function
+ var _trainSet_f = Trainer.prototype._trainSet.toString().replace(/this.network./g, '');
+ _trainSet_f = _trainSet_f.replace('function (set', 'function _trainSet(set') + '\n';
+ _trainSet_f = _trainSet_f.replace('this.crossValidate', 'crossValidate');
+ _trainSet_f = _trainSet_f.replace('crossValidate = true', 'crossValidate = { }');
+
+ // Load and name the test function
+ var test_f = Trainer.prototype.test.toString().replace(/this.network./g, '');
+ test_f = test_f.replace('function (set', 'function test(set') + '\n';
+
+ return Network._SHARED_WORKER_FUNCTIONS = train_f + _trainSet_f + test_f;
+ };
+
+ // rebuild a network that has been stored in a json using the method toJSON()
+ Network.fromJSON = function(json) {
+
+ var neurons = [];
+
+ var layers = {
+ input: new Layer(),
+ hidden: [],
+ output: new Layer()
+ };
+
+ for (var i in json.neurons) {
+ var config = json.neurons[i];
+
+ var neuron = new Neuron();
+ neuron.trace.elegibility = {};
+ neuron.trace.extended = {};
+ neuron.state = config.state;
+ neuron.old = config.old;
+ neuron.activation = config.activation;
+ neuron.bias = config.bias;
+ neuron.squash = config.squash in Neuron.squash ? Neuron.squash[config.squash] : Neuron.squash.LOGISTIC;
+ neurons.push(neuron);
+
+ if (config.layer == 'input')
+ layers.input.add(neuron);
+ else if (config.layer == 'output')
+ layers.output.add(neuron);
+ else {
+ if (typeof layers.hidden[config.layer] == 'undefined')
+ layers.hidden[config.layer] = new Layer();
+ layers.hidden[config.layer].add(neuron);
+ }
+ }
+
+ for (var i in json.connections) {
+ var config = json.connections[i];
+ var from = neurons[config.from];
+ var to = neurons[config.to];
+ var weight = config.weight;
+ var gater = neurons[config.gater];
+
+ var connection = from.project(to, weight);
+ if (gater)
+ gater.gate(connection);
+ }
+
+ return new Network(layers);
+ };
+
+ /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(2)(module)))
+
+/***/ },
+/* 5 */
+/***/ function(module, exports, __webpack_require__) {
+
+ /* WEBPACK VAR INJECTION */(function(module) {// export
+ if (module) module.exports = Trainer;
+
+ /*******************************************************************************************
+ TRAINER
+ *******************************************************************************************/
+
+ function Trainer(network, options) {
+ options = options || {};
+ this.network = network;
+ this.rate = options.rate || .2;
+ this.iterations = options.iterations || 100000;
+ this.error = options.error || .005;
+ this.cost = options.cost || null;
+ this.crossValidate = options.crossValidate || null;
+ }
+
+ Trainer.prototype = {
+
+ // trains any given set to a network
+ train: function(set, options) {
+
+ var error = 1;
+ var iterations = bucketSize = 0;
+ var abort = false;
+ var currentRate;
+ var cost = options && options.cost || this.cost || Trainer.cost.MSE;
+ var crossValidate = false, testSet, trainSet;
+
+ var start = Date.now();
+
+ if (options) {
+ if (options.shuffle) {
+ //+ Jonas Raoni Soares Silva
+ //@ http://jsfromhell.com/array/shuffle [v1.0]
+ function shuffle(o) { //v1.0
+ for (var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
+ return o;
+ };
+ }
+ if (options.iterations)
+ this.iterations = options.iterations;
+ if (options.error)
+ this.error = options.error;
+ if (options.rate)
+ this.rate = options.rate;
+ if (options.cost)
+ this.cost = options.cost;
+ if (options.schedule)
+ this.schedule = options.schedule;
+ if (options.customLog){
+ // for backward compatibility with code that used customLog
+ console.log('Deprecated: use schedule instead of customLog')
+ this.schedule = options.customLog;
+ }
+ if (this.crossValidate || options.crossValidate) {
+ if(!this.crossValidate) this.crossValidate = {};
+ crossValidate = true;
+ if (options.crossValidate.testSize)
+ this.crossValidate.testSize = options.crossValidate.testSize;
+ if (options.crossValidate.testError)
+ this.crossValidate.testError = options.crossValidate.testError;
+ }
+ }
+
+ currentRate = this.rate;
+ if(Array.isArray(this.rate)) {
+ var bucketSize = Math.floor(this.iterations / this.rate.length);
+ }
+
+ if(crossValidate) {
+ var numTrain = Math.ceil((1 - this.crossValidate.testSize) * set.length);
+ trainSet = set.slice(0, numTrain);
+ testSet = set.slice(numTrain);
+ }
+
+ var lastError = 0;
+ while ((!abort && iterations < this.iterations && error > this.error)) {
+ if (crossValidate && error <= this.crossValidate.testError) {
+ break;
+ }
+
+ var currentSetSize = set.length;
+ error = 0;
+ iterations++;
+
+ if(bucketSize > 0) {
+ var currentBucket = Math.floor(iterations / bucketSize);
+ currentRate = this.rate[currentBucket] || currentRate;
+ }
+
+ if(typeof this.rate === 'function') {
+ currentRate = this.rate(iterations, lastError);
+ }
+
+ if (crossValidate) {
+ this._trainSet(trainSet, currentRate, cost);
+ error += this.test(testSet).error;
+ currentSetSize = 1;
+ } else {
+ error += this._trainSet(set, currentRate, cost);
+ currentSetSize = set.length;
+ }
+
+ // check error
+ error /= currentSetSize;
+ lastError = error;
+
+ if (options) {
+ if (this.schedule && this.schedule.every && iterations %
+ this.schedule.every == 0)
+ abort = this.schedule.do({ error: error, iterations: iterations, rate: currentRate });
+ else if (options.log && iterations % options.log == 0) {
+ console.log('iterations', iterations, 'error', error, 'rate', currentRate);
+ };
+ if (options.shuffle)
+ shuffle(set);
+ }
+ }
+
+ var results = {
+ error: error,
+ iterations: iterations,
+ time: Date.now() - start
+ };
+
+ return results;
+ },
+
+ // trains any given set to a network, using a WebWorker (only for the browser). Returns a Promise of the results.
+ trainAsync: function(set, options) {
+ var train = this.workerTrain.bind(this);
+ return new Promise(function(resolve, reject) {
+ try {
+ train(set, resolve, options, true)
+ } catch(e) {
+ reject(e)
+ }
+ })
+ },
+
+ // preforms one training epoch and returns the error (private function used in this.train)
+ _trainSet: function(set, currentRate, costFunction) {
+ var errorSum = 0;
+ for (var train in set) {
+ var input = set[train].input;
+ var target = set[train].output;
+
+ var output = this.network.activate(input);
+ this.network.propagate(currentRate, target);
+
+ errorSum += costFunction(target, output);
+ }
+ return errorSum;
+ },
+
+ // tests a set and returns the error and elapsed time
+ test: function(set, options) {
+
+ var error = 0;
+ var input, output, target;
+ var cost = options && options.cost || this.cost || Trainer.cost.MSE;
+
+ var start = Date.now();
+
+ for (var test in set) {
+ input = set[test].input;
+ target = set[test].output;
+ output = this.network.activate(input);
+ error += cost(target, output);
+ }
+
+ error /= set.length;
+
+ var results = {
+ error: error,
+ time: Date.now() - start
+ };
+
+ return results;
+ },
+
+ // trains any given set to a network using a WebWorker [deprecated: use trainAsync instead]
+ workerTrain: function(set, callback, options, suppressWarning) {
+
+ if (!suppressWarning) {
+ console.warn('Deprecated: do not use `workerTrain`, use `trainAsync` instead.')
+ }
+ var that = this;
+
+ if (!this.network.optimized)
+ this.network.optimize();
+
+ // Create a new worker
+ var worker = this.network.worker(this.network.optimized.memory, set, options);
+
+ // train the worker
+ worker.onmessage = function(e) {
+ switch(e.data.action) {
+ case 'done':
+ var iterations = e.data.message.iterations;
+ var error = e.data.message.error;
+ var time = e.data.message.time;
+
+ that.network.optimized.ownership(e.data.memoryBuffer);
+
+ // Done callback
+ callback({
+ error: error,
+ iterations: iterations,
+ time: time
+ });
+
+ // Delete the worker and all its associated memory
+ worker.terminate();
+ break;
+
+ case 'log':
+ console.log(e.data.message);
+
+ case 'schedule':
+ if (options && options.schedule && typeof options.schedule.do === 'function') {
+ var scheduled = options.schedule.do
+ scheduled(e.data.message)
+ }
+ break;
+ }
+ };
+
+ // Start the worker
+ worker.postMessage({action: 'startTraining'});
+ },
+
+ // trains an XOR to the network
+ XOR: function(options) {
+
+ if (this.network.inputs() != 2 || this.network.outputs() != 1)
+ throw new Error("Incompatible network (2 inputs, 1 output)");
+
+ var defaults = {
+ iterations: 100000,
+ log: false,
+ shuffle: true,
+ cost: Trainer.cost.MSE
+ };
+
+ if (options)
+ for (var i in options)
+ defaults[i] = options[i];
+
+ return this.train([{
+ input: [0, 0],
+ output: [0]
+ }, {
+ input: [1, 0],
+ output: [1]
+ }, {
+ input: [0, 1],
+ output: [1]
+ }, {
+ input: [1, 1],
+ output: [0]
+ }], defaults);
+ },
+
+ // trains the network to pass a Distracted Sequence Recall test
+ DSR: function(options) {
+ options = options || {};
+
+ var targets = options.targets || [2, 4, 7, 8];
+ var distractors = options.distractors || [3, 5, 6, 9];
+ var prompts = options.prompts || [0, 1];
+ var length = options.length || 24;
+ var criterion = options.success || 0.95;
+ var iterations = options.iterations || 100000;
+ var rate = options.rate || .1;
+ var log = options.log || 0;
+ var schedule = options.schedule || {};
+ var cost = options.cost || this.cost || Trainer.cost.CROSS_ENTROPY;
+
+ var trial, correct, i, j, success;
+ trial = correct = i = j = success = 0;
+ var error = 1,
+ symbols = targets.length + distractors.length + prompts.length;
+
+ var noRepeat = function(range, avoid) {
+ var number = Math.random() * range | 0;
+ var used = false;
+ for (var i in avoid)
+ if (number == avoid[i])
+ used = true;
+ return used ? noRepeat(range, avoid) : number;
+ };
+
+ var equal = function(prediction, output) {
+ for (var i in prediction)
+ if (Math.round(prediction[i]) != output[i])
+ return false;
+ return true;
+ };
+
+ var start = Date.now();
+
+ while (trial < iterations && (success < criterion || trial % 1000 != 0)) {
+ // generate sequence
+ var sequence = [],
+ sequenceLength = length - prompts.length;
+ for (i = 0; i < sequenceLength; i++) {
+ var any = Math.random() * distractors.length | 0;
+ sequence.push(distractors[any]);
+ }
+ var indexes = [],
+ positions = [];
+ for (i = 0; i < prompts.length; i++) {
+ indexes.push(Math.random() * targets.length | 0);
+ positions.push(noRepeat(sequenceLength, positions));
+ }
+ positions = positions.sort();
+ for (i = 0; i < prompts.length; i++) {
+ sequence[positions[i]] = targets[indexes[i]];
+ sequence.push(prompts[i]);
+ }
+
+ //train sequence
+ var distractorsCorrect;
+ var targetsCorrect = distractorsCorrect = 0;
+ error = 0;
+ for (i = 0; i < length; i++) {
+ // generate input from sequence
+ var input = [];
+ for (j = 0; j < symbols; j++)
+ input[j] = 0;
+ input[sequence[i]] = 1;
+
+ // generate target output
+ var output = [];
+ for (j = 0; j < targets.length; j++)
+ output[j] = 0;
+
+ if (i >= sequenceLength) {
+ var index = i - sequenceLength;
+ output[indexes[index]] = 1;
+ }
+
+ // check result
+ var prediction = this.network.activate(input);
+
+ if (equal(prediction, output))
+ if (i < sequenceLength)
+ distractorsCorrect++;
+ else
+ targetsCorrect++;
+ else {
+ this.network.propagate(rate, output);
+ }
+
+ error += cost(output, prediction);
+
+ if (distractorsCorrect + targetsCorrect == length)
+ correct++;
+ }
+
+ // calculate error
+ if (trial % 1000 == 0)
+ correct = 0;
+ trial++;
+ var divideError = trial % 1000;
+ divideError = divideError == 0 ? 1000 : divideError;
+ success = correct / divideError;
+ error /= length;
+
+ // log
+ if (log && trial % log == 0)
+ console.log("iterations:", trial, " success:", success, " correct:",
+ correct, " time:", Date.now() - start, " error:", error);
+ if (schedule.do && schedule.every && trial % schedule.every == 0)
+ schedule.do({
+ iterations: trial,
+ success: success,
+ error: error,
+ time: Date.now() - start,
+ correct: correct
+ });
+ }
+
+ return {
+ iterations: trial,
+ success: success,
+ error: error,
+ time: Date.now() - start
+ }
+ },
+
+ // train the network to learn an Embeded Reber Grammar
+ ERG: function(options) {
+
+ options = options || {};
+ var iterations = options.iterations || 150000;
+ var criterion = options.error || .05;
+ var rate = options.rate || .1;
+ var log = options.log || 500;
+ var cost = options.cost || this.cost || Trainer.cost.CROSS_ENTROPY;
+
+ // gramar node
+ var Node = function() {
+ this.paths = [];
+ };
+ Node.prototype = {
+ connect: function(node, value) {
+ this.paths.push({
+ node: node,
+ value: value
+ });
+ return this;
+ },
+ any: function() {
+ if (this.paths.length == 0)
+ return false;
+ var index = Math.random() * this.paths.length | 0;
+ return this.paths[index];
+ },
+ test: function(value) {
+ for (var i in this.paths)
+ if (this.paths[i].value == value)
+ return this.paths[i];
+ return false;
+ }
+ };
+
+ var reberGrammar = function() {
+
+ // build a reber grammar
+ var output = new Node();
+ var n1 = (new Node()).connect(output, "E");
+ var n2 = (new Node()).connect(n1, "S");
+ var n3 = (new Node()).connect(n1, "V").connect(n2, "P");
+ var n4 = (new Node()).connect(n2, "X");
+ n4.connect(n4, "S");
+ var n5 = (new Node()).connect(n3, "V");
+ n5.connect(n5, "T");
+ n2.connect(n5, "X");
+ var n6 = (new Node()).connect(n4, "T").connect(n5, "P");
+ var input = (new Node()).connect(n6, "B");
+
+ return {
+ input: input,
+ output: output
+ }
+ };
+
+ // build an embeded reber grammar
+ var embededReberGrammar = function() {
+ var reber1 = reberGrammar();
+ var reber2 = reberGrammar();
+
+ var output = new Node();
+ var n1 = (new Node).connect(output, "E");
+ reber1.output.connect(n1, "T");
+ reber2.output.connect(n1, "P");
+ var n2 = (new Node).connect(reber1.input, "P").connect(reber2.input,
+ "T");
+ var input = (new Node).connect(n2, "B");
+
+ return {
+ input: input,
+ output: output
+ }
+
+ };
+
+ // generate an ERG sequence
+ var generate = function() {
+ var node = embededReberGrammar().input;
+ var next = node.any();
+ var str = "";
+ while (next) {
+ str += next.value;
+ next = next.node.any();
+ }
+ return str;
+ };
+
+ // test if a string matches an embeded reber grammar
+ var test = function(str) {
+ var node = embededReberGrammar().input;
+ var i = 0;
+ var ch = str.charAt(i);
+ while (i < str.length) {
+ var next = node.test(ch);
+ if (!next)
+ return false;
+ node = next.node;
+ ch = str.charAt(++i);
+ }
+ return true;
+ };
+
+ // helper to check if the output and the target vectors match
+ var different = function(array1, array2) {
+ var max1 = 0;
+ var i1 = -1;
+ var max2 = 0;
+ var i2 = -1;
+ for (var i in array1) {
+ if (array1[i] > max1) {
+ max1 = array1[i];
+ i1 = i;
+ }
+ if (array2[i] > max2) {
+ max2 = array2[i];
+ i2 = i;
+ }
+ }
+
+ return i1 != i2;
+ };
+
+ var iteration = 0;
+ var error = 1;
+ var table = {
+ "B": 0,
+ "P": 1,
+ "T": 2,
+ "X": 3,
+ "S": 4,
+ "E": 5
+ };
+
+ var start = Date.now();
+ while (iteration < iterations && error > criterion) {
+ var i = 0;
+ error = 0;
+
+ // ERG sequence to learn
+ var sequence = generate();
+
+ // input
+ var read = sequence.charAt(i);
+ // target
+ var predict = sequence.charAt(i + 1);
+
+ // train
+ while (i < sequence.length - 1) {
+ var input = [];
+ var target = [];
+ for (var j = 0; j < 6; j++) {
+ input[j] = 0;
+ target[j] = 0;
+ }
+ input[table[read]] = 1;
+ target[table[predict]] = 1;
+
+ var output = this.network.activate(input);
+
+ if (different(output, target))
+ this.network.propagate(rate, target);
+
+ read = sequence.charAt(++i);
+ predict = sequence.charAt(i + 1);
+
+ error += cost(target, output);
+ }
+ error /= sequence.length;
+ iteration++;
+ if (iteration % log == 0) {
+ console.log("iterations:", iteration, " time:", Date.now() - start,
+ " error:", error);
+ }
+ }
+
+ return {
+ iterations: iteration,
+ error: error,
+ time: Date.now() - start,
+ test: test,
+ generate: generate
+ }
+ },
+
+ timingTask: function(options){
+
+ if (this.network.inputs() != 2 || this.network.outputs() != 1)
+ throw new Error("Invalid Network: must have 2 inputs and one output");
+
+ if (typeof options == 'undefined')
+ options = {};
+
+ // helper
+ function getSamples (trainingSize, testSize){
+
+ // sample size
+ var size = trainingSize + testSize;
+
+ // generate samples
+ var t = 0;
+ var set = [];
+ for (var i = 0; i < size; i++) {
+ set.push({ input: [0,0], output: [0] });
+ }
+ while(t < size - 20) {
+ var n = Math.round(Math.random() * 20);
+ set[t].input[0] = 1;
+ for (var j = t; j <= t + n; j++){
+ set[j].input[1] = n / 20;
+ set[j].output[0] = 0.5;
+ }
+ t += n;
+ n = Math.round(Math.random() * 20);
+ for (var k = t+1; k <= (t + n) && k < size; k++)
+ set[k].input[1] = set[t].input[1];
+ t += n;
+ }
+
+ // separate samples between train and test sets
+ var trainingSet = []; var testSet = [];
+ for (var l = 0; l < size; l++)
+ (l < trainingSize ? trainingSet : testSet).push(set[l]);
+
+ // return samples
+ return {
+ train: trainingSet,
+ test: testSet
+ }
+ }
+
+ var iterations = options.iterations || 200;
+ var error = options.error || .005;
+ var rate = options.rate || [.03, .02];
+ var log = options.log === false ? false : options.log || 10;
+ var cost = options.cost || this.cost || Trainer.cost.MSE;
+ var trainingSamples = options.trainSamples || 7000;
+ var testSamples = options.trainSamples || 1000;
+
+ // samples for training and testing
+ var samples = getSamples(trainingSamples, testSamples);
+
+ // train
+ var result = this.train(samples.train, {
+ rate: rate,
+ log: log,
+ iterations: iterations,
+ error: error,
+ cost: cost
+ });
+
+ return {
+ train: result,
+ test: this.test(samples.test)
+ }
+ }
+ };
+
+ // Built-in cost functions
+ Trainer.cost = {
+ // Eq. 9
+ CROSS_ENTROPY: function(target, output)
+ {
+ var crossentropy = 0;
+ for (var i in output)
+ crossentropy -= (target[i] * Math.log(output[i]+1e-15)) + ((1-target[i]) * Math.log((1+1e-15)-output[i])); // +1e-15 is a tiny push away to avoid Math.log(0)
+ return crossentropy;
+ },
+ MSE: function(target, output)
+ {
+ var mse = 0;
+ for (var i in output)
+ mse += Math.pow(target[i] - output[i], 2);
+ return mse / output.length;
+ },
+ BINARY: function(target, output){
+ var misses = 0;
+ for (var i in output)
+ misses += Math.round(target[i] * 2) != Math.round(output[i] * 2);
+ return misses;
+ }
+ }
+
+ /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(2)(module)))
+
+/***/ },
+/* 6 */
+/***/ function(module, exports, __webpack_require__) {
+
+ /* WEBPACK VAR INJECTION */(function(module) {// import
+ var Layer = __webpack_require__(3)
+ , Network = __webpack_require__(4)
+ , Trainer = __webpack_require__(5)
+
+ /*******************************************************************************************
+ ARCHITECT
+ *******************************************************************************************/
+
+ // Collection of useful built-in architectures
+ var Architect = {
+
+ // Multilayer Perceptron
+ Perceptron: function Perceptron() {
+
+ var args = Array.prototype.slice.call(arguments); // convert arguments to Array
+ if (args.length < 3)
+ throw new Error("not enough layers (minimum 3) !!");
+
+ var inputs = args.shift(); // first argument
+ var outputs = args.pop(); // last argument
+ var layers = args; // all the arguments in the middle
+
+ var input = new Layer(inputs);
+ var hidden = [];
+ var output = new Layer(outputs);
+
+ var previous = input;
+
+ // generate hidden layers
+ for (var level in layers) {
+ var size = layers[level];
+ var layer = new Layer(size);
+ hidden.push(layer);
+ previous.project(layer);
+ previous = layer;
+ }
+ previous.project(output);
+
+ // set layers of the neural network
+ this.set({
+ input: input,
+ hidden: hidden,
+ output: output
+ });
+
+ // trainer for the network
+ this.trainer = new Trainer(this);
+ },
+
+ // Multilayer Long Short-Term Memory
+ LSTM: function LSTM() {
+
+ var args = Array.prototype.slice.call(arguments); // convert arguments to array
+ if (args.length < 3)
+ throw new Error("not enough layers (minimum 3) !!");
+
+ var last = args.pop();
+ var option = {
+ peepholes: Layer.connectionType.ALL_TO_ALL,
+ hiddenToHidden: false,
+ outputToHidden: false,
+ outputToGates: false,
+ inputToOutput: true,
+ };
+ if (typeof last != 'number') {
+ var outputs = args.pop();
+ if (last.hasOwnProperty('peepholes'))
+ option.peepholes = last.peepholes;
+ if (last.hasOwnProperty('hiddenToHidden'))
+ option.hiddenToHidden = last.hiddenToHidden;
+ if (last.hasOwnProperty('outputToHidden'))
+ option.outputToHidden = last.outputToHidden;
+ if (last.hasOwnProperty('outputToGates'))
+ option.outputToGates = last.outputToGates;
+ if (last.hasOwnProperty('inputToOutput'))
+ option.inputToOutput = last.inputToOutput;
+ } else
+ var outputs = last;
+
+ var inputs = args.shift();
+ var layers = args;
+
+ var inputLayer = new Layer(inputs);
+ var hiddenLayers = [];
+ var outputLayer = new Layer(outputs);
+
+ var previous = null;
+
+ // generate layers
+ for (var layer in layers) {
+ // generate memory blocks (memory cell and respective gates)
+ var size = layers[layer];
+
+ var inputGate = new Layer(size).set({
+ bias: 1
+ });
+ var forgetGate = new Layer(size).set({
+ bias: 1
+ });
+ var memoryCell = new Layer(size);
+ var outputGate = new Layer(size).set({
+ bias: 1
+ });
+
+ hiddenLayers.push(inputGate);
+ hiddenLayers.push(forgetGate);
+ hiddenLayers.push(memoryCell);
+ hiddenLayers.push(outputGate);
+
+ // connections from input layer
+ var input = inputLayer.project(memoryCell);
+ inputLayer.project(inputGate);
+ inputLayer.project(forgetGate);
+ inputLayer.project(outputGate);
+
+ // connections from previous memory-block layer to this one
+ if (previous != null) {
+ var cell = previous.project(memoryCell);
+ previous.project(inputGate);
+ previous.project(forgetGate);
+ previous.project(outputGate);
+ }
+
+ // connections from memory cell
+ var output = memoryCell.project(outputLayer);
+
+ // self-connection
+ var self = memoryCell.project(memoryCell);
+
+ // hidden to hidden recurrent connection
+ if (option.hiddenToHidden)
+ memoryCell.project(memoryCell, Layer.connectionType.ALL_TO_ELSE);
+
+ // out to hidden recurrent connection
+ if (option.outputToHidden)
+ outputLayer.project(memoryCell);
+
+ // out to gates recurrent connection
+ if (option.outputToGates) {
+ outputLayer.project(inputGate);
+ outputLayer.project(outputGate);
+ outputLayer.project(forgetGate);
+ }
+
+ // peepholes
+ memoryCell.project(inputGate, option.peepholes);
+ memoryCell.project(forgetGate, option.peepholes);
+ memoryCell.project(outputGate, option.peepholes);
+
+ // gates
+ inputGate.gate(input, Layer.gateType.INPUT);
+ forgetGate.gate(self, Layer.gateType.ONE_TO_ONE);
+ outputGate.gate(output, Layer.gateType.OUTPUT);
+ if (previous != null)
+ inputGate.gate(cell, Layer.gateType.INPUT);
+
+ previous = memoryCell;
+ }
+
+ // input to output direct connection
+ if (option.inputToOutput)
+ inputLayer.project(outputLayer);
+
+ // set the layers of the neural network
+ this.set({
+ input: inputLayer,
+ hidden: hiddenLayers,
+ output: outputLayer
+ });
+
+ // trainer
+ this.trainer = new Trainer(this);
+ },
+
+ // Liquid State Machine
+ Liquid: function Liquid(inputs, hidden, outputs, connections, gates) {
+
+ // create layers
+ var inputLayer = new Layer(inputs);
+ var hiddenLayer = new Layer(hidden);
+ var outputLayer = new Layer(outputs);
+
+ // make connections and gates randomly among the neurons
+ var neurons = hiddenLayer.neurons();
+ var connectionList = [];
+
+ for (var i = 0; i < connections; i++) {
+ // connect two random neurons
+ var from = Math.random() * neurons.length | 0;
+ var to = Math.random() * neurons.length | 0;
+ var connection = neurons[from].project(neurons[to]);
+ connectionList.push(connection);
+ }
+
+ for (var j = 0; j < gates; j++) {
+ // pick a random gater neuron
+ var gater = Math.random() * neurons.length | 0;
+ // pick a random connection to gate
+ var connection = Math.random() * connectionList.length | 0;
+ // let the gater gate the connection
+ neurons[gater].gate(connectionList[connection]);
+ }
+
+ // connect the layers
+ inputLayer.project(hiddenLayer);
+ hiddenLayer.project(outputLayer);
+
+ // set the layers of the network
+ this.set({
+ input: inputLayer,
+ hidden: [hiddenLayer],
+ output: outputLayer
+ });
+
+ // trainer
+ this.trainer = new Trainer(this);
+ },
+
+ Hopfield: function Hopfield(size) {
+
+ var inputLayer = new Layer(size);
+ var outputLayer = new Layer(size);
+
+ inputLayer.project(outputLayer, Layer.connectionType.ALL_TO_ALL);
+
+ this.set({
+ input: inputLayer,
+ hidden: [],
+ output: outputLayer
+ });
+
+ var trainer = new Trainer(this);
+
+ var proto = Architect.Hopfield.prototype;
+
+ proto.learn = proto.learn || function(patterns)
+ {
+ var set = [];
+ for (var p in patterns)
+ set.push({
+ input: patterns[p],
+ output: patterns[p]
+ });
+
+ return trainer.train(set, {
+ iterations: 500000,
+ error: .00005,
+ rate: 1
+ });
+ };
+
+ proto.feed = proto.feed || function(pattern)
+ {
+ var output = this.activate(pattern);
+
+ var pattern = [];
+ for (var i in output)
+ pattern[i] = output[i] > .5 ? 1 : 0;
+
+ return pattern;
+ }
+ }
+ }
+
+ // Extend prototype chain (so every architectures is an instance of Network)
+ for (var architecture in Architect) {
+ Architect[architecture].prototype = new Network();
+ Architect[architecture].prototype.constructor = Architect[architecture];
+ }
+
+ // export
+ if (module) module.exports = Architect;
+ /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(2)(module)))
+
+/***/ }
+/******/ ]);
\ No newline at end of file