pi-emergence
Version:
Various emergent phenomena
390 lines (366 loc) • 18.3 kB
JavaScript
"use strict";
var _activationFunction = _interopRequireDefault(require("../components/activation-function.js"));
var _neuronRunner = _interopRequireDefault(require("../components/neuron-runner.js"));
var _appMatrixNeuro = _interopRequireDefault(require("../apps/app.matrix-neuro.js"));
var _neuronLayer = _interopRequireDefault(require("../components/neuron-layer.js"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
/**
* Logical and visual (Visualogical) representation of a standard FeedForward network.
* The matrix version (which does all the real work) gets converted to this for visual and intuitive representation.
*/
var FeedForwardNueralNetwork = /*#__PURE__*/function () {
function FeedForwardNueralNetwork(app, options) {
_classCallCheck(this, FeedForwardNueralNetwork);
if (!app || typeof app.mounted !== "boolean") throw new Error("FeedForwardNueralNetwork must be created with an app (" + _typeof(app) + ")");
if (!options) options = {};
this.app = app;
this.networkType = 1;
this.layers = options.layers;
if (!Array.isArray(this.layers)) this.layers = [];
this.squashFunction = options.squashFunction;
if (!(options.squashFunction instanceof _activationFunction["default"])) {
this.squashFunction = _activationFunction["default"].sigmoidActivationFunction; // Sigmoid by default
}
this.matrixNetwork = null; // The matrix/math guts for training
this.squash = this.squashFunction.squash;
this.layerCount = this.layers.length;
this.neuronCount = 0;
this.inputLayer = null;
this.outputLayer = null;
// UI - Need to separate this
this.position = null;
this.runners = [];
}
_createClass(FeedForwardNueralNetwork, [{
key: "initWithMatrixNetwork",
value: function initWithMatrixNetwork(matrixNeuroApp) {
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
if (this.layers.length > 0) {
console.log("Skipping init of FeedForwardNueralNetwork because it already has layers");
return;
}
if (!(matrixNeuroApp instanceof _appMatrixNeuro["default"])) throw new Error("Cannot setup from matrix when matrix is not a MatrixNeuroApp");
var i = 0;
var layers = [];
for (i = 0; i < matrixNeuroApp.neuronCounts.length - 1; i++) {
var neuronCount = matrixNeuroApp.neuronCounts[i] + 1; // +1 for bias. The visual feed forward network holds biases as neurons in the feeding layer
var name = i === 0 ? "Input Layer" : "Hidden Layer " + i;
var layerOptions = {
name: name,
neuronCount: neuronCount,
biasCount: 1,
vectorHandler: options === null || options === void 0 ? void 0 : options.vectorHandler,
app: this.app
};
var hiddenLayer = new _neuronLayer["default"](this, layerOptions);
layers.push(hiddenLayer);
}
var outputsCount = matrixNeuroApp.neuronCounts[matrixNeuroApp.neuronCounts.length - 1];
var outputLayer = new _neuronLayer["default"](this, {
name: "Output Layer",
neuronCount: outputsCount,
biasCount: 0
});
layers.push(outputLayer);
this.appendLayers(layers);
this.connect(0);
for (var layerIndex = 1; layerIndex < this.layerCount; layerIndex++) {
var prevIndex = layerIndex - 1;
var currentLayer = this.layers[layerIndex];
var weightMatrix = matrixNeuroApp.weightMatrices[prevIndex]; // row,col => hidden0, input
// Loop through the next layer's neurons
for (var n = 0; n < currentLayer.neuronCount; n++) {
var neuron = currentLayer.neurons[n];
if (neuron.backConnectors.length === 0) break; // Probably the bias neuron if it has no back connectors
var matrixWeights = weightMatrix.items[n]; // <-- These should mirror the back connectors at this point
// Loop through the current neuron's back connectors
var ci = void 0;
for (ci = 0; ci < neuron.backConnectors.length - 1; ci++) {
var connector = neuron.backConnectors[ci];
connector.weight = matrixWeights[ci];
}
// This back connector should be connecting to the bias neuron
var biasWeights = matrixNeuroApp.biases[prevIndex].items[n];
var biasWeight = biasWeights[0];
neuron.backConnectors[ci].weight = biasWeight;
}
}
this.runners = [];
this.matrixNetwork = matrixNeuroApp;
}
}, {
key: "createRunner",
value: function createRunner(neuron) {
var target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
var speed = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 5;
var wayPoints = _neuronRunner["default"].createRandomConnectorMap(neuron);
console.warn("Waypoints: " + wayPoints.length);
var options = {
wayPoints: wayPoints,
neuron: neuron,
speed: speed,
twoWay: true
};
var runner = new _neuronRunner["default"](this, options);
this.runners.push(runner);
runner.run();
console.log("Runner Created: " + runner.id);
return runner;
}
}, {
key: "removeRunner",
value: function removeRunner(runner) {
var index = this.runners.indexOf(runner);
if (index < 0) return false;
this.runners.splice(index, 1);
return true;
}
}, {
key: "setupFromOld",
value: function setupFromOld(matrixNeuroApp) {
if (this.layers.length > 0) throw new Error("Cannot setup from matrix when layers already exist");
if (!(matrixNeuroApp instanceof _appMatrixNeuro["default"])) throw new Error("Cannot setup from matrix when matrix is not a MatrixNeuroApp");
var i = 0;
var j = 0;
var rowIndex, columnIndex;
// Create the first layer (input layer)
var layers = [new _neuronLayer["default"](this, {
name: "Input Layer",
neuronCount: matrixNeuroApp.input_nodes + 1,
biasCount: 1
})];
// Create the hidden layers
for (i = 0; i < matrixNeuroApp.hidden_nodes.length; i++) {
var hiddenLayerNeuronCount = matrixNeuroApp.hidden_nodes[i] + 1; // +1 for bias
var hiddenLayer = new _neuronLayer["default"](this, {
name: "Hidden Layer " + i,
neuronCount: hiddenLayerNeuronCount,
biasCount: 1
});
layers.push(hiddenLayer);
}
// Create the output layers
var outputLayer = new _neuronLayer["default"](this, {
name: "Output Layer",
neuronCount: matrixNeuroApp.output_nodes
});
layers.push(outputLayer);
// Add the layers and connect
this.appendLayers(layers);
this.connect(0);
console.log("Connected with " + this.layers.length + " layers [" + _typeof(matrixNeuroApp.hidden_nodes[0]) + "]");
// ** Set the weights for inputs => hidden0
var firstHiddenLayerNeuronCount = matrixNeuroApp.hidden_nodes[0];
for (rowIndex = 0; rowIndex < firstHiddenLayerNeuronCount; rowIndex++) {
var hiddenNeuron = this.layers[1].neurons[rowIndex];
for (columnIndex = 0; columnIndex < hiddenNeuron.backConnectors.length - 1; columnIndex++) {
var inputToHidden0Row = matrixNeuroApp.weights_ih.items[rowIndex]; // row,col => hidden0, input
var weight = inputToHidden0Row[columnIndex];
if (typeof weight !== "number") throw new Error("Invalid weight: " + weight + " (" + _typeof(weight) + ")");
hiddenNeuron.backConnectors[columnIndex].weight = weight;
}
}
// Add the bias weight to the input => hidden0 layer
var hidden0BiasMatrix = matrixNeuroApp.bias_h[0];
var inputBiasNeuron = this.layers[0].neurons[this.layers[0].neurons.length - 1];
for (i = 0; i < firstHiddenLayerNeuronCount; i++) {
inputBiasNeuron.forwardConnectors[i].weight = hidden0BiasMatrix.items[i][0];
}
// ** Set weights for inner hidden layers.
// ** Technically, there may not be any, but not sure how useful a 3-layer nn would be (input => one hidden => output) but I've seen stranger things ** /
console.log("Converting inner hidden layer (" + matrixNeuroApp.weights_hh.length + ")");
for (i = 1; i < matrixNeuroApp.hidden_nodes.length; i++) {
var idx = i - 1;
var hiddenNeuronCount = matrixNeuroApp.hidden_nodes[i];
for (rowIndex = 0; rowIndex < hiddenNeuronCount; rowIndex++) {
var _hiddenNeuron = this.layers[i + 1].neurons[rowIndex]; // row
for (columnIndex = 0; columnIndex < _hiddenNeuron.backConnectors.length - 1; columnIndex++) {
var prevToCurrentRow = matrixNeuroApp.weights_hh[idx].items[rowIndex]; // row,col => hidden0, input
var _weight = prevToCurrentRow[columnIndex];
if (typeof _weight !== "number") throw new Error("Invalid weight: " + _weight + " (" + _typeof(_weight) + ")");
_hiddenNeuron.backConnectors[columnIndex].weight = _weight;
}
}
// Add the biases weight to the hidden[n - 1] => hidden[n] layer
var _hidden0BiasMatrix = matrixNeuroApp.bias_h[idx];
var _biasNeuron = this.layers[i].neurons[this.layers[i].neurons.length - 1]; // Last neuron in the layer
for (i = 0; i < hiddenNeuronCount; i++) {
_biasNeuron.forwardConnectors[i].weight = _hidden0BiasMatrix.items[i][0];
}
}
// ** Final hidden layer => output layer conversion ---------------------- ** /
// Set the weights for hidden0 => output
var lastHiddenLayer = this.layers[this.layers.length - 2];
var nnLastHiddenLayerNeuronCount = matrixNeuroApp.hidden_nodes.length > 0 ? matrixNeuroApp.hidden_nodes[matrixNeuroApp.hidden_nodes.length - 1] : matrixNeuroApp.input_nodes;
console.log("Converting hidden.last[" + nnLastHiddenLayerNeuronCount + "] => output layer");
for (i = 0; i < matrixNeuroApp.output_nodes; i++) {
var matrixRow = matrixNeuroApp.weights_ho.items[i];
var outputNeuron = this.outputLayer.neurons[j];
for (j = 0; j < nnLastHiddenLayerNeuronCount; j++) {
var weightMatrix = matrixRow[j];
outputNeuron.backConnectors[j].weight = weightMatrix;
}
}
// Add the bias weight to the hidden0 => output layer
var biasNeuron = lastHiddenLayer.neurons[lastHiddenLayer.neurons.length - 1];
for (i = 0; i < this.outputLayer.neuronCount; i++) {
for (var n = 0; n < matrixNeuroApp.output_nodes; n++) {
var baisWeightMatrix = matrixNeuroApp.bias_o.items[i][n];
biasNeuron.forwardConnectors[n].weight = baisWeightMatrix;
}
}
this.matrixNetwork = matrixNeuroApp;
}
}, {
key: "appendLayer",
value: function appendLayer(layer) {
if (!layer) throw new Error("Invalid layer to append");
this.appendLayers([layer]);
return layer;
}
}, {
key: "appendLayers",
value: function appendLayers(layers) {
var _this$layers;
if (!Array.isArray(layers)) throw new Error("Invalid layers to append. Try appendLayer(layer) for non-array layers");
(_this$layers = this.layers).push.apply(_this$layers, _toConsumableArray(layers));
this.refreshLayout();
return layers;
}
}, {
key: "insertLayer",
value: function insertLayer(layer, atIndex) {
if (!layer) throw new Error("Invalid layer to insert");
return this.insertLayers([layer], atIndex);
}
}, {
key: "insertLayers",
value: function insertLayers(layers, atIndex) {
var _this$layers2;
if (atIndex < 0 || atIndex >= this.layerCount === 0) return this.appendLayers(layers);
if (!Array.isArray(layers)) throw new Error("Invalid layers to insert. Try insertLayer(layer) for non-array layers");
(_this$layers2 = this.layers).splice.apply(_this$layers2, [atIndex, 0].concat(_toConsumableArray(layers)));
this.refreshLayout();
return layers;
}
/**
* Connects all of the layers with connectors
*/
}, {
key: "connect",
value: function connect() {
var initialWeightValue = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
for (var i = 0; i < this.layerCount; i++) this.layers[i].connect(initialWeightValue);
}
}, {
key: "setWeightMatrix",
value: function setWeightMatrix(matrix) {
//
}
}, {
key: "randomizeWeights",
value: function randomizeWeights() {
for (var i = 0; i < this.layerCount; i++) this.layers[i].randomizeWeights();
}
}, {
key: "execute",
value: function execute(inputs) {
if (!Array.isArray(inputs)) throw new Error("Invalid inputs to submit");
if (inputs.length !== this.inputLayer.neuronCount) {
if (this.inputLayer.neuronCount - inputs.length === 1) inputs.push(1.0);else throw new Error("Invalid input count. Expected " + this.inputLayer.neuronCount + " but got " + inputs.length);
}
// Copy to input layer
for (var i = 0; i < inputs.length; i++) {
var n = this.inputLayer.neurons[i];
n.value = inputs[i];
n.rawValue = n.value;
}
for (var _i = 1; _i < this.layerCount; _i++) {
this.layers[_i].activate();
}
return this.outputLayer.getValues();
}
}, {
key: "executeMatrix",
value: function executeMatrix(inputs) {
if (!this.matrixNetwork) throw new Error("No neural network to execute");
return this.matrixNetwork.test(inputs, true);
}
}, {
key: "trainXor",
value: function trainXor() {
var epocs = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 100;
var maxErrorValue = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0.05;
return 0;
}
}, {
key: "reset",
value: function reset() {
for (var i = 0; i < this.layerCount; i++) {
this.layers[i].reset();
}
}
}, {
key: "refreshLayout",
value: function refreshLayout() {
this.layerCount = this.layers.length;
this.inputLayer = this.layers[0];
this.outputLayer = this.layers[this.layerCount - 1];
var nc = 0;
var i = 0;
for (i = 0; i < this.layerCount; i++) {
var layer = this.layers[i];
layer.index = i;
layer.setLayout();
nc += layer.neurons.length;
}
this.neuronCount = nc;
}
}, {
key: "updatePositions",
value: function updatePositions() {
// Update the positions of the layers
var i;
var layerCount = this.layerCount;
for (i = 0; i < layerCount; i++) {
this.layers[i].updatePositions();
}
for (i = 0; i < this.runners.length; i++) {
var runner = this.runners[i];
runner.updatePosition();
}
return layerCount;
}
}, {
key: "draw",
value: function draw() {
var i = 0;
for (i = 0; i < this.layers.length; i++) {
// Draw the layer
this.layers[i].draw();
}
for (i = 0; i < this.runners.length; i++) {
var runner = this.runners[i];
runner.draw();
}
}
}]);
return FeedForwardNueralNetwork;
}();
if (typeof module === 'undefined') {
console.log("Can't export. Running FeedForwardNueralNetwork in-browser");
} else {
module.exports = FeedForwardNueralNetwork;
}