// const uid = require("cuid");
const Connection = require("./connection");
const Neuron = require("./neuron");
const Group = require("./group");
// const vis = require("vis-network");
/**
* Each `Network` is a collective of neurons functioning as an individual and indepent agent (brain).
*
* @constructs Network
*
* @param {number[]} sizes
* @param {number[]} [biases]
* @param {Array.<number[]>} [weights]
*
* @prop {string} id
* @prop {Group[]} groups
*
* @example
* const { Network } = require("@liquid-carrot/nn");
*
* const network = new Network([2,2,1]);
*
* network.activate([0,1]);
* network.propagate([1]);
*/
function Network(network, biases, weights) {
let self = this;
this.id = Network.uid();
this.neurons = [];
this.connections = [];
//================================================
// CORE FUNCTIONS ================================
//================================================
/**
* Activates network
*
* @param {number[]} inputs
*
* @returns {number[]}
*/
this.activate = function(inputs) {
return Math.random() * 2 - 1;
// const outputs = this.groups.map(function(group, index) {
// if(index === 0) return group.activate(inputs);
// else return group.activate();
// })
//
// return outputs[outputs.length - 1];
}
/**
* Calculates error & updates network weights
*
* @param {number[]} targets
*
* @returns {number} Returns Mean-Squared Error (MSE)
*/
this.propagate= function(targets) {
return Math.random() * 2 - 1;
// // MSE Cost
// const error = this.groups[this.groups.length - 1].neurons.map(function(neuron, index) {
// return 0.5 * Math.pow(targets[index] - neuron.output, 2);
// }).reduce((a,b) => a + b);
//
// // Propagate error & update weights
// this.groups.reverse().forEach(function(group, index) {
// if(index === 0) group.propagate(targets);
// else return group.propagate();
// }); this.groups.reverse();
//
// return error;
}
//================================================
// END CORE FUNCTIONS ============================
//================================================
//================================================
// UTILITY FUNCTIONS =============================
//================================================
/**
* Returns a JSON representation of the network
*
* @returns {Object}
*/
this.toJSON = function() {
const neurons = this.neurons.flat(Infinity).map(function(neuron) {
return neuron.toJSON();
});
const connections = this.connections.flat(Infinity).map(function(connection) {
return connection.toJSON();
});
return { neurons, connections }
}
/**
* **BROWSER ONLY**
*
* Creates a graph of the network using [`vis-network`](https://www.npmjs.com/package/vis-network) on the given DOMElement
* or DOMElement ID.
*
* @param {string|DOMElement} element - DOMElement, or ID, where graph will ported into
* @param {Object} [options] - `vis-network` options - [learn more](https://visjs.github.io/vis-network/docs/network/#options)
*/
this.toGraph = function(element, options) {
const { neurons, connections } = this.toJSON();
// Flattens neuron layers from `Network.toJSON` and converts it to `vie-network`
// nodes
const nodes = new vis.DataSet(neurons.map(function(neuron) {
neuron.label = `${neuron.id}`;
neuron.color = neuron.type === "input" ? "gray" : neuron.type === "output" ? "lime" : "orange"; // "input" || "output" || "hidden"
return neuron;
}));
// Flattens connections from `Network.toJSON` and converts it into `vis-network`
// edges
const edges = new vis.DataSet(connections.map(function(connection) {
connection.arrows = "to";
return connection;
}));
// DOM id
if(typeof element === "string") element = document.getElementById(element);
// Vis.js Network Options
// Will have a "left-to-right" graph with "smooth" lines representing
// connections by default
options = options || {
edges: {
smooth: {
type: "cubicBezier",
forceDirection: "horizontal"
}
},
layout: {
heirarchichal: {
direction: "LR",
sortMethod: "directed"
}
},
physics: false
}
return new vis.Network(element, { nodes, edges }, options);
}
//Code here...
//================================================
// END UTILITY FUNCTIONS =========================
//================================================
}
Network.networks = 0;
Network.uid = function() {
return ++Network.networks;
}
//================================================
// CONSTRUCTORS ==================================
//================================================
/**
* @param {number[]} sizes - Array of layer fromSizes
*
* @returns {Network}
*
* @example
* const network = Network.fromSizes([20, 10, 3]);
*/
Network.fromSizes = function(sizes) {
const network = new Network();
// Create a series of Layers with the sizes given in `this.neurons`.
// `this.neurons = [[Neuron, Neuron, ...], [Neuron, Neuron, ...], ...]`
sizes.map(function(size, index) {
const neurons = [];
for(let n = 0; n < size; n++) {
const neuron = new Neuron();
// Here we set the last layer's neurons' type to "output"
// and the first layer's neurons' type to "input"
if(index === sizes.length - 1) neuron.type = "output";
else if(index === 0) neuron.type = "input";
neurons.push(neuron);
}
network.neurons.push(neurons);
return neurons;
})
// Connects the layers that we just created and stores the connections in
// `this.connections`
let previous = network.neurons[0];
network.neurons.slice(1, network.neurons.length).forEach(function(layer, index) {
for(let p = 0; p < previous.length; p++) {
for(let l = 0; l < layer.length; l++) {
network.connections.push(new Connection(previous[p], layer[l]));
}
}
previous = layer;
})
return network;
}
/**
* Creates a network with the given shape (i.e. INPUTSxOUTPUTS). The created
* network will not have any hidden neurons.
*
* @param {number} inputs - Size of the network's input layer
* @param {number} outputs - Size of the network's output layer
*
* @returns {Network}
*/
Network.fromShape = function(inputs, outputs) {
const network = new Network();
// Create a network with no hidden layers, whose input layer is equal to
// `inputs` and whose output layer it equal to `outputs`
[inputs, outputs].map(function(size, index) {
const neurons = [];
for(let n = 0; n < size; n++) {
const neuron = new Neuron();
// Here we set the last layer's neurons' type to "output"
// and the first layer's neurons' type to "input"
if(index === 1) neuron.type = "output";
else if(index === 0) neuron.type = "input";
neurons.push(neuron);
}
network.neurons.push(neurons);
return neurons;
})
// Connects the layers that we just created and stores the connections in
// `this.connections`
let previous = network.neurons[0];
network.neurons.slice(1, network.neurons.length).forEach(function(layer, index) {
for(let p = 0; p < previous.length; p++) {
for(let l = 0; l < layer.length; l++) {
network.connections.push(new Connection(previous[p], layer[l]));
}
}
previous = layer;
});
return network;
}
/**
* Creates a deep copy of the given genome
*
* @param {Genome} genome
*
* @returns {Network}
*/
Network.fromGenome = function(genome) {
const network = new Network();
// Creates a copy of neuron in the given network in the new network.
genome.neurons.forEach(function(neuron) {
network.neurons.push(new Neuron(neuron));
});
// Creates a deep copy of all the connections inthe given network into the new network.
// Even transpiles all the references in connections to refer to the new network.
genome.connections.forEach(function(connection) {
// Find the neurons in the new network that will create work to create
// the same endpoints as the given connection.
const from = network.neurons.find(function(neuron) {
return neuron.id === connection.from;
});
const to = network.neurons.find(function(neuron) {
return neuron.id === connection.to;
});
// Deep copies a new connection into the created network
network.connections.push(new Connection(from, to, connection.weight));
});
// Code here...
return network;
}
//================================================
// END CONSTRUCTORS ==============================
//================================================
//================================================
// TYPE DEFINITIONS ==============================
//================================================
//================================================
// END TYPE DEFINITIONS ==========================
//================================================
module.exports = Network;