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 + + + + + + + + + + + Canvas is not supported by your browser + + + + + + + \ 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