🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

neatjs

Package Overview
Dependencies
Maintainers
1
Versions
14
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

neatjs - npm Package Compare versions

Comparing version
0.2.6
to
0.3.0-1
+25
-16
component.json
{
"name": "neatjs",
"version": "0.3.0-1",
"description": "NeuroEvolution of Augmenting Topologies (NEAT) implemented in Javascript (with tests done in Mocha for verification). Can be used as a node module or in a browser",
"repo": "OptimusLime/neatjs",
"description": "NeuroEvolution of Augmenting Topologies (NEAT) implemented in Javascript (with tests done in Mocha for verification). Can be used as a node module or in a browser",
"version": "0.2.6",
"keywords": ["Neural Network", "NN", "Artificial Neural Networks", "ANN", "CPPN", "NEAT", "HyperNEAT"],
"keywords": [
"Neural Network",
"NN",
"Artificial Neural Networks",
"ANN",
"CPPN",
"NEAT",
"HyperNEAT"
],
"dependencies": {
"OptimusLime/cppnjs" : "*"
"optimuslime/cppnjs": "*",
"optimuslime/win-utils": "*"
},

@@ -15,14 +24,14 @@ "development": {},

"neat.js",
"evolution/iec.js",
"evolution/multiobjective.js",
"evolution/novelty.js",
"genome/neatConnection.js",
"genome/neatNode.js",
"genome/neatGenome.js",
"neatHelp/neatDecoder.js",
"neatHelp/neatHelp.js",
"neatHelp/neatParameters.js",
"types/nodeType.js",
"utility/genomeSharpToJS.js"
"evolution/iec.js",
"evolution/multiobjective.js",
"evolution/novelty.js",
"genome/neatConnection.js",
"genome/neatNode.js",
"genome/neatGenome.js",
"neatHelp/neatDecoder.js",
"neatHelp/neatHelp.js",
"neatHelp/neatParameters.js",
"types/nodeType.js",
"utility/genomeSharpToJS.js"
]
}
}

@@ -27,7 +27,9 @@

self.gid = (typeof gid === 'string' ? parseFloat(gid) : gid);
//gid must be a string
self.gid = typeof gid === "number" ? "" + gid : gid;//(typeof gid === 'string' ? parseFloat(gid) : gid);
self.weight = (typeof weight === 'string' ? parseFloat(weight) : weight);
self.sourceID = (typeof srcTgtObj.sourceID === 'string' ? parseFloat(srcTgtObj.sourceID) : srcTgtObj.sourceID);
self.targetID = (typeof srcTgtObj.targetID === 'string' ? parseFloat(srcTgtObj.targetID) : srcTgtObj.targetID);
//node ids are strings now -- so make sure to save as string always
self.sourceID = (typeof srcTgtObj.sourceID === 'number' ? "" + (srcTgtObj.sourceID) : srcTgtObj.sourceID);
self.targetID = (typeof srcTgtObj.targetID === 'number' ? "" + (srcTgtObj.targetID) : srcTgtObj.targetID);

@@ -34,0 +36,0 @@ //learning rates and modulatory information contained here, not generally used or tested

@@ -25,3 +25,4 @@ /**

self.gid = (typeof gid === 'string' ? parseFloat(gid) : gid);
//gids are strings not numbers -- make it so
self.gid = typeof gid === "number" ? "" + gid : gid;
//we only story the string of the activation funciton

@@ -28,0 +29,0 @@ //let cppns deal with actual act functions

/**
* Module dependencies.
*/
//none
var uuid = require('win-utils').cuid;
/**

@@ -52,3 +51,3 @@ * Expose `neatHelp`.

{
var prevInnovationId= -1;
var prevInnovationId= "";

@@ -73,3 +72,3 @@ var self = this;

{
if(correlationItem.connection1.gid<=prevInnovationId)
if(uuid.isLessThan(correlationItem.connection1.gid, prevInnovationId) || correlationItem.connection1.gid == prevInnovationId)
return false;

@@ -81,3 +80,3 @@

{
if(correlationItem.connection2.gid<=prevInnovationId)
if(uuid.isLessThan(correlationItem.connection2.gid, prevInnovationId) || correlationItem.connection2.gid == prevInnovationId)
return false;

@@ -100,3 +99,3 @@

// Innovation ID's should be in order and not duplicated.
if(correlationItem.connection1.gid <=prevInnovationId)
if(uuid.isLessThan(correlationItem.connection1.gid, prevInnovationId) || correlationItem.connection1.gid == prevInnovationId)
return false;

@@ -103,0 +102,0 @@

{
"name": "neatjs",
"description": "NeuroEvolution of Augmenting Topologies (NEAT) implemented in Javascript (with tests done in Mocha for verification). Can be used as a node module or in a browser",
"keywords": ["Neural Network", "NN", "Artificial Neural Networks", "ANN", "CPPN", "NEAT", "HyperNEAT"],
"author" : { "name": "Paul Szerlip",
"email": "Paul.Szerlip@cs.ucf.edu",
"url": "http://designforcode.com/" },
"version": "0.2.6",
"main":"neat.js",
"repository": {
"type": "git",
"url": "https://github.com/OptimusLime/neatjs.git"
},
"bugs" : "https://github.com/OptimusLime/neatjs/issues",
"license":"MIT",
"dependencies": {
"cppnjs" : "0.x.x"
},
"devDependencies": {
"mocha": "1.1.x",
"should": "1.1.x",
"xml2js" : "0.2.x"
}
}
"name": "neatjs",
"version": "0.3.0-1",
"description": "NeuroEvolution of Augmenting Topologies (NEAT) implemented in Javascript (with tests done in Mocha for verification). Can be used as a node module or in a browser",
"keywords": [
"Neural Network",
"NN",
"Artificial Neural Networks",
"ANN",
"CPPN",
"NEAT",
"HyperNEAT"
],
"author": {
"name": "Paul Szerlip",
"email": "Paul.Szerlip@cs.ucf.edu",
"url": "http://designforcode.com/"
},
"main": "neat.js",
"repository": {
"type": "git",
"url": "https://github.com/OptimusLime/neatjs.git"
},
"bugs": "https://github.com/OptimusLime/neatjs/issues",
"license": "MIT",
"dependencies": {
"cppnjs": "0.x.x",
"win-utils": "*"
},
"devDependencies": {
"mocha": "1.1.x",
"should": "1.1.x",
"xml2js": "0.2.x"
}
}

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

var utils = require('../utility/utilities.js');
var cppnActivationFunctions = require('./cppnActivationFunctions.js');
var Factory = {};
module.exports = Factory;
Factory.probabilities = [];
Factory.functions = [];
Factory.functionTable= {};
Factory.createActivationFunction = function(functionID)
{
if(!cppnActivationFunctions[functionID])
throw new Error("Activation Function doesn't exist!");
// For now the function ID is the name of a class that implements IActivationFunction.
return new cppnActivationFunctions[functionID]();
};
Factory.getActivationFunction = function(functionID)
{
var activationFunction = Factory.functionTable[functionID];
if(!activationFunction)
{
// console.log('Creating: ' + functionID);
// console.log('ActivationFunctions: ');
// console.log(cppnActivationFunctions);
activationFunction = Factory.createActivationFunction(functionID);
Factory.functionTable[functionID] = activationFunction;
}
return activationFunction;
};
Factory.setProbabilities = function(oProbs)
{
Factory.probabilities = [];//new double[probs.Count];
Factory.functions = [];//new IActivationFunction[probs.Count];
var counter = 0;
for(var key in oProbs)
{
Factory.probabilities.push(oProbs[key]);
Factory.functions.push(Factory.getActivationFunction(key));
counter++;
}
};
Factory.defaultProbabilities = function()
{
var oProbs = {'BipolarSigmoid' :.25, 'Sine':.25, 'Gaussian':.25, 'Linear':.25};
Factory.setProbabilities(oProbs);
};
Factory.getRandomActivationFunction = function()
{
if(Factory.probabilities.length == 0)
Factory.defaultProbabilities();
return Factory.functions[utils.RouletteWheel.singleThrowArray(Factory.probabilities)];
};
var cppnActivationFunctions = {};
module.exports = cppnActivationFunctions;
//implemented the following:
//BipolarSigmoid
//PlainSigmoid
//Gaussian
//Linear
//NullFn
//Sine
//StepFunction
cppnActivationFunctions.ActivationFunction = function(functionObj)
{
var self = this;
self.functionID = functionObj.functionID;
self.functionString = functionObj.functionString;
self.functionDescription = functionObj.functionDescription;
self.calculate = functionObj.functionCalculate;
self.enclose = functionObj.functionEnclose;
// console.log('self.calc');
// console.log(self.calculate);
// console.log(self.calculate(0));
};
//this makes it easy to overwrite an activation function from the outside
//cppnActivationFunctions.AddActivationFunction("BiplorSigmoid", {NEW IMPLEMENTATION});
//this can be useful for customizing certain functions for your domain while maintaining the same names
cppnActivationFunctions.AddActivationFunction = function(functionName, description)
{
cppnActivationFunctions[functionName] = function()
{
return new cppnActivationFunctions.ActivationFunction(description);
}
};
cppnActivationFunctions.AddActivationFunction(
"BipolarSigmoid",
{
functionID: 'BipolarSigmoid' ,
functionString: "2.0/(1.0 + exp(-4.9*inputSignal)) - 1.0",
functionDescription: "bipolar steepend sigmoid",
functionCalculate: function(inputSignal)
{
return (2.0 / (1.0 + Math.exp(-4.9 * inputSignal))) - 1.0;
},
functionEnclose: function(stringToEnclose)
{
return "((2.0 / (1.0 + Math.exp(-4.9 *(" + stringToEnclose + ")))) - 1.0)";
}
});
cppnActivationFunctions.AddActivationFunction(
"PlainSigmoid",
{
functionID: 'PlainSigmoid' ,
functionString: "1.0/(1.0+(exp(-inputSignal)))",
functionDescription: "Plain sigmoid [xrange -5.0,5.0][yrange, 0.0,1.0]",
functionCalculate: function(inputSignal)
{
return 1.0/(1.0+(Math.exp(-inputSignal)));
},
functionEnclose: function(stringToEnclose)
{
return "(1.0/(1.0+(Math.exp(-1.0*(" + stringToEnclose + ")))))";
}
});
cppnActivationFunctions.AddActivationFunction(
"Gaussian",
{
functionID: 'Gaussian',
functionString: "2*e^(-(input*2.5)^2) - 1",
functionDescription:"bimodal gaussian",
functionCalculate: function(inputSignal)
{
return 2 * Math.exp(-Math.pow(inputSignal * 2.5, 2)) - 1;
},
functionEnclose: function(stringToEnclose)
{
return "(2.0 * Math.exp(-Math.pow(" + stringToEnclose + "* 2.5, 2.0)) - 1.0)";
}
});
cppnActivationFunctions.AddActivationFunction(
"Linear",
{
functionID: 'Linear',
functionString: "Math.abs(x)",
functionDescription:"Linear",
functionCalculate: function(inputSignal)
{
return Math.abs(inputSignal);
},
functionEnclose: function(stringToEnclose)
{
return "(Math.abs(" + stringToEnclose + "))";
}
});
cppnActivationFunctions.AddActivationFunction(
"NullFn",
{
functionID: 'NullFn',
functionString: "0",
functionDescription: "returns 0",
functionCalculate: function(inputSignal)
{
return 0.0;
},
functionEnclose: function(stringToEnclose)
{
return "(0.0)";
}
});
cppnActivationFunctions.AddActivationFunction(
"Sine2",
{
functionID: 'Sine2',
functionString: "Sin(2*inputSignal)",
functionDescription: "Sine function with doubled period",
functionCalculate: function(inputSignal)
{
return Math.sin(2*inputSignal);
},
functionEnclose: function(stringToEnclose)
{
return "(Math.sin(2.0*(" + stringToEnclose + ")))";
}
});
cppnActivationFunctions.AddActivationFunction(
"Sine",
{
functionID: 'Sine',
functionString: "Sin(inputSignal)",
functionDescription: "Sine function with normal period",
functionCalculate: function(inputSignal)
{
return Math.sin(inputSignal);
},
functionEnclose: function(stringToEnclose)
{
return "(Math.sin(" + stringToEnclose + "))";
}
});
cppnActivationFunctions.AddActivationFunction(
"StepFunction",
{
functionID: 'StepFunction',
functionString: "x<=0 ? 0.0 : 1.0",
functionDescription: "Step function [xrange -5.0,5.0][yrange, 0.0,1.0]",
functionCalculate: function(inputSignal)
{
if(inputSignal<=0.0)
return 0.0;
else
return 1.0;
},
functionEnclose: function(stringToEnclose)
{
return "(((" + stringToEnclose + ') <= 0.0) ? 0.0 : 1.0)';
}
});
{
"name": "cppnjs",
"repo": "OptimusLime/cppnjs",
"description": "Compositional Pattern Producing Networks in javascript (type of artificial neural network)",
"version": "0.2.4",
"keywords": [
"Neural Networks",
"ANN",
"CPPN",
"NEAT"
],
"dependencies": {},
"development": {},
"license": "MIT",
"paths": [
"activationFunctions",
"networks",
"extras",
"types",
"utility"
],
"main": "cppn.js",
"scripts": [
"cppn.js",
"activationFunctions/cppnActivationFactory.js",
"activationFunctions/cppnActivationFunctions.js",
"networks/cppnConnection.js",
"networks/cppnNode.js",
"networks/cppn.js",
"types/nodeType.js",
"utility/utilities.js",
"extras/adaptableAdditions.js",
"extras/pureCPPNAdditions.js",
"extras/gpuAdditions.js"
]
}
var cppnjs = {};
//export the cppn library
module.exports = cppnjs;
//CPPNs
cppnjs.cppn = require('./networks/cppn.js');
//nodes and connections!
cppnjs.cppnNode = require('./networks/cppnNode.js');
cppnjs.cppnConnection = require('./networks/cppnConnection.js');
//all the activations your heart could ever hope for
cppnjs.cppnActivationFunctions = require('./activationFunctions/cppnActivationFunctions.js');
cppnjs.cppnActivationFactory = require('./activationFunctions/cppnActivationFactory.js');
//and the utilities to round it out!
cppnjs.utilities = require('./utility/utilities.js');
//exporting the node type
cppnjs.NodeType = require('./types/nodeType.js');
//The purpose of this file is to only extend CPPNs to have additional activation capabilities involving mod connections
var cppnConnection = require("../networks/cppnConnection.js");
//default all the variables that need to be added to handle adaptable activation
var connectionPrototype = cppnConnection.prototype;
connectionPrototype.a = 0;
connectionPrototype.b = 0;
connectionPrototype.c = 0;
connectionPrototype.d = 0;
connectionPrototype.modConnection = 0;
connectionPrototype.learningRate = 0;
var CPPN = require("../networks/cppn");
//default all the variables that need to be added to handle adaptable activation
var cppnPrototype = CPPN.prototype;
cppnPrototype.a = 0;
cppnPrototype.b = 0;
cppnPrototype.c = 0;
cppnPrototype.d = 0;
cppnPrototype.learningRate = 0;
cppnPrototype.pre = 0;
cppnPrototype.post = 0;
cppnPrototype.adaptable = false;
cppnPrototype.modulatory = false;
/// <summary>
/// This function carries out a single network activation.
/// It is called by all those methods that require network activations.
/// </summary>
/// <param name="maxAllowedSignalDelta">
/// The network is not relaxed as long as the absolute value of the change in signals at any given point is greater than this value.
/// Only positive values are used. If the value is less than or equal to 0, the method will return true without checking for relaxation.
/// </param>
/// <returns>True if the network is relaxed, or false if not.</returns>
cppnPrototype.singleStepInternal = function(maxAllowedSignalDelta)
{
var isRelaxed = true; // Assume true.
var self = this;
// Calculate each connection's output signal, and add the signals to the target neurons.
for (var i = 0; i < self.connections.length; i++) {
if (self.adaptable)
{
if (self.connections[i].modConnection <= 0.0) //Normal connection
{
self.neuronSignalsBeingProcessed[self.connections[i].targetIdx] += self.neuronSignals[self.connections[i].sourceIdx] * self.connections[i].weight;
}
else //modulatory connection
{
self.modSignals[self.connections[i].targetIdx] += self.neuronSignals[self.connections[i].sourceIdx] * self.connections[i].weight;
}
}
else
{
self.neuronSignalsBeingProcessed[self.connections[i].targetIdx] += self.neuronSignals[self.connections[i].sourceIdx] * self.connections[i].weight;
}
}
// Pass the signals through the single-valued activation functions.
// Do not change the values of input neurons or neurons that have no activation function because they are part of a module.
for (var i = self.totalInputNeuronCount; i < self.neuronSignalsBeingProcessed.length; i++) {
self.neuronSignalsBeingProcessed[i] = self.activationFunctions[i].calculate(self.neuronSignalsBeingProcessed[i]+self.biasList[i]);
if (self.modulatory)
{
//Make sure it's between 0 and 1
self.modSignals[i] += 1.0;
if (self.modSignals[i]!=0.0)
self.modSignals[i] = utilities.tanh(self.modSignals[i]);//Tanh(modSignals[i]);//(Math.Exp(2 * modSignals[i]) - 1) / (Math.Exp(2 * modSignals[i]) + 1));
}
}
//TODO Modules not supported in this implementation - don't care
/*foreach (float f in neuronSignals)
HyperNEATParameters.distOutput.Write(f.ToString("R") + " ");
HyperNEATParameters.distOutput.WriteLine();
HyperNEATParameters.distOutput.Flush();*/
// Move all the neuron signals we changed while processing this network activation into storage.
if (maxAllowedSignalDelta > 0) {
for (var i = self.totalInputNeuronCount; i < self.neuronSignalsBeingProcessed.length; i++) {
// First check whether any location in the network has changed by more than a small amount.
isRelaxed &= (Math.abs(self.neuronSignals[i] - self.neuronSignalsBeingProcessed[i]) > maxAllowedSignalDelta);
self.neuronSignals[i] = self.neuronSignalsBeingProcessed[i];
self.neuronSignalsBeingProcessed[i] = 0.0;
}
} else {
for (var i = self.totalInputNeuronCount; i < self.neuronSignalsBeingProcessed.length; i++) {
self.neuronSignals[i] = self.neuronSignalsBeingProcessed[i];
self.neuronSignalsBeingProcessed[i] = 0.0;
}
}
// Console.WriteLine(inputNeuronCount);
if (self.adaptable)//CPPN != null)
{
var coordinates = [0,0,0,0];
var modValue;
var weightDelta;
for (var i = 0; i < self.connections.length; i++)
{
if (self.modulatory)
{
self.pre = self.neuronSignals[self.connections[i].sourceIdx];
self.post = self.neuronSignals[self.connections[i].targetIdx];
modValue = self.modSignals[self.connections[i].targetIdx];
self.a = self.connections[i].a;
self.b = self.connections[i].b;
self.c = self.connections[i].c;
self.d = self.connections[i].d;
self.learningRate = self.connections[i].learningRate;
if (modValue != 0.0 && (self.connections[i].modConnection <= 0.0)) //modulate target neuron if its a normal connection
{
self.connections[i].weight += modValue*self.learningRate * (self.a * self.pre * self.post + self.b * self.pre + self.c * self.post + self.d);
}
if (Math.abs(self.connections[i].weight) > 5.0)
{
self.connections[i].weight = 5.0 * Math.sign(self.connections[i].weight);
}
}
else
{
self.pre = self.neuronSignals[self.connections[i].sourceIdx];
self.post = self.neuronSignals[self.connections[i].targetIdx];
self.a = self.connections[i].a;
self.b = self.connections[i].b;
self.c = self.connections[i].c;
self.learningRate = self.connections[i].learningRate;
weightDelta = self.learningRate * (self.a * self.pre * self.post + self.b * self.pre + self.c * self.post);
connections[i].weight += weightDelta;
// Console.WriteLine(pre + " " + post + " " + learningRate + " " + A + " " + B + " " + C + " " + weightDelta);
if (Math.abs(self.connections[i].weight) > 5.0)
{
self.connections[i].weight = 5.0 * Math.sign(self.connections[i].weight);
}
}
}
}
for (var i = self.totalInputNeuronCount; i < self.neuronSignalsBeingProcessed.length; i++)
{
self.modSignals[i] = 0.0;
}
return isRelaxed;
};
cppnPrototype.singleStep = function(finished)
{
var self = this;
self.singleStepInternal(0.0); // we will ignore the value of this function, so the "allowedDelta" argument doesn't matter.
if (finished)
{
finished(null);
}
};
cppnPrototype.multipleSteps = function(numberOfSteps)
{
var self = this;
for (var i = 0; i < numberOfSteps; i++) {
self.singleStep();
}
};
//this takes in cppn functions, and outputs a shader....
//radical!
//needs to be tested more! How large can CPPNs get? inputs/outputs/hiddens?
//we'll extend a CPPN to produce a GPU shader
var CPPN = require("../networks/cppn");
var CPPNPrototype = CPPN.prototype;
var cppnToGPU = {};
cppnToGPU.ShaderFragments = {};
cppnToGPU.ShaderFragments.passThroughVariables =
[
"uniform float texelWidth;",
"uniform float texelHeight;"
].join('\n');
//simple, doesn't do anything but pass on uv coords to the frag shaders
cppnToGPU.ShaderFragments.passThroughVS =
[
cppnToGPU.ShaderFragments.passThroughVariables,
"varying vec2 passCoord;",
"void main() {",
"passCoord = uv;",
"gl_Position = vec4( position, 1.0 );",
"}",
"\n"
].join('\n');
cppnToGPU.ShaderFragments.passThroughVS3x3 =
[
cppnToGPU.ShaderFragments.passThroughVariables,
"varying vec2 sampleCoords[9];",
"void main() {",
"gl_Position = vec4( position, 1.0 );",
"vec2 widthStep = vec2(texelWidth, 0.0);",
"vec2 heightStep = vec2(0.0, texelHeight);",
"vec2 widthHeightStep = vec2(texelWidth, texelHeight);",
"vec2 widthNegativeHeightStep = vec2(texelWidth, -texelHeight);",
"vec2 inputTextureCoordinate = uv;",
"sampleCoords[0] = inputTextureCoordinate.xy;",
"sampleCoords[1] = inputTextureCoordinate.xy - widthStep;",
"sampleCoords[2] = inputTextureCoordinate.xy + widthStep;",
"sampleCoords[3] = inputTextureCoordinate.xy - heightStep;",
"sampleCoords[4] = inputTextureCoordinate.xy - widthHeightStep;",
"sampleCoords[5] = inputTextureCoordinate.xy + widthNegativeHeightStep;",
"sampleCoords[6] = inputTextureCoordinate.xy + heightStep;",
"sampleCoords[7] = inputTextureCoordinate.xy - widthNegativeHeightStep;",
"sampleCoords[8] = inputTextureCoordinate.xy + widthHeightStep;",
"}",
"\n"
].join('\n');
cppnToGPU.ShaderFragments.variables =
[
"varying vec2 passCoord; ",
"uniform sampler2D inputTexture; "
].join('\n');
cppnToGPU.ShaderFragments.variables3x3 =
[
"varying vec2 sampleCoords[9];",
"uniform sampler2D inputTexture; "
].join('\n');
//this is a generic conversion from cppn to shader
//Extends the CPPN object to have fullShaderFromCPPN function (and a callback to add any extras)
//the extras will actually dictate how the final output is created and used
CPPNPrototype.fullShaderFromCPPN = function(specificAddFunction)
{
var cppn = this;
// console.log('Decoded!');
// console.log('Start enclose :)');
var functionObject = cppn.createPureCPPNFunctions();
// console.log('End enclose!');
//functionobject of the form
// {contained: contained, stringFunctions: stringFunctions, arrayIdentifier: "this.rf", nodeOrder: inOrderAct};
var multiInput = cppn.inputNeuronCount >= 27;
var totalNeurons = cppn.totalNeuronCount;
var inorderString = "";
var lastIx = functionObject.nodeOrder[totalNeurons-1];
functionObject.nodeOrder.forEach(function(ix)
{
inorderString += ix + (ix !== lastIx ? "," : "");
});
var defaultVariables = multiInput ? cppnToGPU.ShaderFragments.variables3x3 : cppnToGPU.ShaderFragments.variables;
//create a float array the size of the neurons
// var fixedArrayDec = "int order[" + totalNeurons + "](" + inorderString + ");";
var arrayDeclaration = "float register[" + totalNeurons + "];";
var beforeFunctionIx = "void f";
var functionWrap = "(){";
var postFunctionWrap = "}";
var repString = functionObject.arrayIdentifier;
var fns = functionObject.stringFunctions;
var wrappedFunctions = [];
for(var key in fns)
{
if(key < cppn.totalInputNeuronCount)
continue;
//do this as 3 separate lines
var wrap = beforeFunctionIx + key + functionWrap;
wrappedFunctions.push(wrap);
var setRegister = "register[" + key + "] = ";
var repFn = fns[key].replace(new RegExp(repString, 'g'), "register");
//remove all Math. references -- e.g. Math.sin == sin in gpu code
repFn = repFn.replace(new RegExp("Math.", 'g'), "");
//we don't want a return function, fs are void
repFn = repFn.replace(new RegExp("return ", 'g'), "");
//anytime you see a +-, this actually means -
//same goes for -- this is a +
repFn = repFn.replace(new RegExp("\\+\\-", 'g'), "-");
repFn = repFn.replace(new RegExp("\\-\\-", 'g'), "+");
wrappedFunctions.push(setRegister + repFn);
wrappedFunctions.push(postFunctionWrap);
}
var activation = [];
var actBefore, additionalParameters;
if(cppn.outputNeuronCount == 1)
{
actBefore = "float";
additionalParameters = "";
}
else
{
// actBefore = "float[" + ng.outputNodeCount + "]";
actBefore = "void";
additionalParameters = ", out float[" + cppn.outputNeuronCount + "] outputs";
}
actBefore += " activate(float[" +cppn.inputNeuronCount + "] fnInputs" + additionalParameters+ "){";
activation.push(actBefore);
var bCount = cppn.biasNeuronCount;
for(var i=0; i < bCount; i++)
{
activation.push('register[' + i + '] = 1.0;');
}
for(var i=0; i < cppn.inputNeuronCount; i++)
{
activation.push('register[' + (i + bCount) + '] = fnInputs[' + i + '];');
}
functionObject.nodeOrder.forEach(function(ix)
{
if(ix >= cppn.totalInputNeuronCount)
activation.push("f"+ix +"();");
});
var outputs;
//if you're just one output, return a simple float
//otherwise, you need to return an array
if(cppn.outputNeuronCount == 1)
{
outputs = "return register[" + cppn.totalInputNeuronCount + "];";
}
else
{
var multiOut = [];
// multiOut.push("float o[" + ng.outputNodeCount + "];");
for(var i=0; i < cppn.outputNeuronCount; i++)
multiOut.push("outputs[" + i + "] = register[" + (i + cppn.totalInputNeuronCount) + "];");
// multiOut.push("return o;");
outputs = multiOut.join('\n');
}
activation.push(outputs);
activation.push("}");
var additional = specificAddFunction(cppn);
return {vertex: multiInput ?
cppnToGPU.ShaderFragments.passThroughVS3x3 :
cppnToGPU.ShaderFragments.passThroughVS,
fragment: [defaultVariables,arrayDeclaration].concat(wrappedFunctions).concat(activation).concat(additional).join('\n')};
};
//The purpose of this file is to only extend CPPNs to have additional activation capabilities involving turning
//cppns into a string!
var CPPN = require("../networks/cppn");
//for convenience, you can require pureCPPNAdditions
module.exports = CPPN;
var CPPNPrototype = CPPN.prototype;
CPPNPrototype.createPureCPPNFunctions = function()
{
var self = this;
//create our enclosed object for each node! (this way we actually have subnetworks functions setup too
self.nEnclosed = new Array(self.neuronSignals.length);
self.bAlreadyEnclosed = new Array(self.neuronSignals.length);
self.inEnclosure = new Array(self.neuronSignals.length);
// Initialize boolean arrays and set the last activation signal, but only if it isn't an input (these have already been set when the input is activated)
for (var i = 0; i < self.nEnclosed.length; i++)
{
// Set as activated if i is an input node, otherwise ensure it is unactivated (false)
self.bAlreadyEnclosed[i] = (i < self.totalInputNeuronCount) ? true : false;
self.nEnclosed[i] = (i < self.totalInputNeuronCount ? "x" + i : "");
self.inEnclosure[i] = false;
}
// Get each output node activation recursively
// NOTE: This is an assumption that genomes have started minimally, and the output nodes lie sequentially after the input nodes
for (var i = 0; i < self.outputNeuronCount; i++){
// for (var m = 0; m < self.nEnclosed.length; m++)
// {
// // Set as activated if i is an input node, otherwise ensure it is unactivated (false)
// self.bAlreadyEnclosed[m] = (m < self.totalInputNeuronCount) ? true : false;
// self.inEnclosure[m] = false;
// }
self.nrEncloseNode(self.totalInputNeuronCount + i);
}
// console.log(self.nEnclosed);
//now grab our ordered objects
var orderedObjects = self.recursiveCountThings();
// console.log(orderedObjects);
//now let's build our functions
var nodeFunctions = {};
var stringFunctions = {};
var emptyNodes = {};
for(var i= self.totalInputNeuronCount; i < self.totalNeuronCount; i++)
{
//skip functions that aren't defined
if(!self.bAlreadyEnclosed[i]){
emptyNodes[i] = true;
continue;
}
var fnString = "return " + self.nEnclosed[i] + ';';
nodeFunctions[i] = new Function([], fnString);
stringFunctions[i] = fnString;
}
var inOrderAct = [];
//go through and grab the indices -- no need for rank and things
orderedObjects.forEach(function(oNode)
{
if(!emptyNodes[oNode.node])
inOrderAct.push(oNode.node);
});
var containedFunction = function(nodesInOrder, functionsForNodes, biasCount, outputCount)
{
return function(inputs)
{
var bias = 1.0;
var context = {};
context.rf = new Array(nodesInOrder.length);
var totalIn = inputs.length + biasCount;
for(var i=0; i < biasCount; i++)
context.rf[i] = bias;
for(var i=0; i < inputs.length; i++)
context.rf[i+biasCount] = inputs[i];
for(var i=0; i < nodesInOrder.length; i++)
{
var fIx = nodesInOrder[i];
// console.log('Ix to hit: ' fIx + );
context.rf[fIx] = (fIx < totalIn ? context.rf[fIx] : functionsForNodes[fIx].call(context));
}
return context.rf.slice(totalIn, totalIn + outputCount);
}
};
//this will return a function that can be run by calling var outputs = functionName(inputs);
var contained = containedFunction(inOrderAct, nodeFunctions, self.biasNeuronCount, self.outputNeuronCount);
return {contained: contained, stringFunctions: stringFunctions, arrayIdentifier: "this.rf", nodeOrder: inOrderAct};
// console.log(self.nEnclosed[self.totalInputNeuronCount + 0].length);
// console.log('Enclosed nodes: ');
// console.log(self.nEnclosed);
// console.log('Ordered: ');
// console.log(orderedActivation);
};
CPPNPrototype.nrEncloseNode = function(currentNode)
{
var self = this;
// If we've reached an input node we return since the signal is already set
// console.log('Checking: ' + currentNode);
// console.log('Total: ');
// console.log(self.totalInputNeuronCount);
if (currentNode < self.totalInputNeuronCount)
{
self.inEnclosure[currentNode] = false;
self[currentNode] = 'this.rf[' + currentNode + ']';
return;
}
if (self.bAlreadyEnclosed[currentNode])
{
self.inEnclosure[currentNode] = false;
return;
}
// Mark that the node is currently being calculated
self.inEnclosure[currentNode] = true;
// Adjacency list in reverse holds incoming connections, go through each one and activate it
for (var i = 0; i < self.reverseAdjacentList[currentNode].length; i++)
{
var crntAdjNode = self.reverseAdjacentList[currentNode][i];
//{ Region recurrant connection handling - not applicable in our implementation
// If this node is currently being activated then we have reached a cycle, or recurrant connection. Use the previous activation in this case
if (self.inEnclosure[crntAdjNode])
{
//easy fix, this isn't meant for recurrent networks -- just throw an error!
throw new Error("Method not built for recurrent networks!");
}
// Otherwise proceed as normal
else
{
// Recurse if this neuron has not been activated yet
if (!self.bAlreadyEnclosed[crntAdjNode])
self.nrEncloseNode(crntAdjNode);
// console.log('Next: ');
// console.log(crntAdjNode);
// console.log(self.nEnclosed[crntAdjNode]);
var add = (self.nEnclosed[currentNode] == "" ? "(" : "+");
//get our weight from adjacency matrix
var weight = self.adjacentMatrix[crntAdjNode][currentNode];
//we have a whole number weight!
if(Math.round(weight) === weight)
weight = '' + weight + '.0';
else
weight = '' + weight;
// Add it to the new activation
self.nEnclosed[currentNode] += add + weight + "*" + "this.rf[" + crntAdjNode + "]";
}
//} endregion
// nodeCount++;
}
//if we're empty, we're empty! We don't go no where, derrrr
if(self.nEnclosed[currentNode] === '')
self.nEnclosed[currentNode] = '0.0';
else
self.nEnclosed[currentNode] += ')';
// Mark this neuron as completed
self.bAlreadyEnclosed[currentNode] = true;
// This is no longer being calculated (for cycle detection)
self.inEnclosure[currentNode] = false;
// console.log('Enclosed legnth: ' + self.activationFunctions[currentNode].enclose(self.nEnclosed[currentNode]).length);
self.nEnclosed[currentNode] = self.activationFunctions[currentNode].enclose(self.nEnclosed[currentNode]);
};
CPPNPrototype.recursiveCountThings = function()
{
var self= this;
var orderedActivation = {};
var higherLevelRecurse = function(neuron)
{
var inNode = new Array(self.totalNeuronCount);
var nodeCount = new Array(self.totalNeuronCount);
var interactCount = new Array(self.totalNeuronCount);
for(var s=0; s < self.totalNeuronCount; s++) {
inNode[s] = false;
nodeCount[s] = 0;
interactCount[s] = 0;
}
var recurseNode = function(currentNode)
{
// Mark that the node is currently being calculated
inNode[currentNode] = true;
var recurse = {};
// Adjacency list in reverse holds incoming connections, go through each one and activate it
for (var i = 0; i < self.reverseAdjacentList[currentNode].length; i++)
{
var crntAdjNode = self.reverseAdjacentList[currentNode][i];
recurse[i] = (nodeCount[crntAdjNode] < nodeCount[currentNode] + 1);
nodeCount[crntAdjNode] = Math.max(nodeCount[crntAdjNode], nodeCount[currentNode] + 1);
}
//all nodes are marked with correct count, let's continue backwards for each one!
for (var i = 0; i < self.reverseAdjacentList[currentNode].length; i++)
{
var crntAdjNode = self.reverseAdjacentList[currentNode][i];
if(recurse[i])
// Recurse on it! -- already marked above
recurseNode(crntAdjNode);
}
// nodeCount[currentNode] = nodeCount[currentNode] + 1;
inNode[currentNode] = false;
};
recurseNode(neuron);
return nodeCount;
};
var orderedObjects = new Array(self.totalNeuronCount);
// Get each output node activation recursively
// NOTE: This is an assumption that genomes have started minimally, and the output nodes lie sequentially after the input nodes
for (var m = 0; m < self.outputNeuronCount; m++){
//we have ordered count for this output!
var olist = higherLevelRecurse(self.totalInputNeuronCount + m);
var nodeSpecificOrdering = [];
for(var n=0; n< olist.length; n++)
{
//we take the maximum depending on whether or not it's been seen before
if(orderedObjects[n])
orderedObjects[n] = {node: n, rank: Math.max(orderedObjects[n].rank, olist[n])};
else
orderedObjects[n] = {node: n, rank: olist[n]};
nodeSpecificOrdering.push({node: n, rank: olist[n]});
}
nodeSpecificOrdering.sort(function(a,b){return b.rank - a.rank;});
orderedActivation[self.totalInputNeuronCount + m] = nodeSpecificOrdering;
}
orderedObjects.sort(function(a,b){return b.rank - a.rank;});
// console.log(orderedObjects);
return orderedObjects;
};
/**
* Module dependencies.
*/
var utilities = require('../utility/utilities.js');
/**
* Expose `CPPN`.
*/
module.exports = CPPN;
/**
* Initialize a new error view.
*
* @param {Number} biasNeuronCount
* @param {Number} inputNeuronCount
* @param {Number} outputNeuronCount
* @param {Number} totalNeuronCount
* @param {Array} connections
* @param {Array} biasList
* @param {Array} activationFunctions
* @api public
*/
function CPPN(
biasNeuronCount,
inputNeuronCount,
outputNeuronCount,
totalNeuronCount,
connections,
biasList,
activationFunctions
)
{
var self = this;
// must be in the same order as neuronSignals. Has null entries for neurons that are inputs or outputs of a module.
self.activationFunctions = activationFunctions;
// The modules and connections are in no particular order; only the order of the neuronSignals is used for input and output methods.
//floatfastconnections
self.connections = connections;
/// The number of bias neurons, usually one but sometimes zero. This is also the index of the first input neuron in the neuron signals.
self.biasNeuronCount = biasNeuronCount;
/// The number of input neurons.
self.inputNeuronCount = inputNeuronCount;
/// The number of input neurons including any bias neurons. This is also the index of the first output neuron in the neuron signals.
self.totalInputNeuronCount = self.biasNeuronCount + self.inputNeuronCount;
/// The number of output neurons.
self.outputNeuronCount = outputNeuronCount;
//save the total neuron count for us
self.totalNeuronCount = totalNeuronCount;
// For the following array, neurons are ordered with bias nodes at the head of the list,
// then input nodes, then output nodes, and then hidden nodes in the array's tail.
self.neuronSignals = [];
self.modSignals = [];
// This array is a parallel of neuronSignals, and only has values during SingleStepInternal().
// It is declared here to avoid having to reallocate it for every network activation.
self.neuronSignalsBeingProcessed = [];
//initialize the neuron,mod, and processing signals
for(var i=0; i < totalNeuronCount; i++){
//either you are 1 for bias, or 0 otherwise
self.neuronSignals.push(i < self.biasNeuronCount ? 1 : 0);
self.modSignals.push(0);
self.neuronSignalsBeingProcessed.push(0);
}
self.biasList = biasList;
// For recursive activation, marks whether we have finished this node yet
self.activated = [];
// For recursive activation, makes whether a node is currently being calculated. For recurrant connections
self.inActivation = [];
// For recursive activation, the previous activation for recurrent connections
self.lastActivation = [];
self.adjacentList = [];
self.reverseAdjacentList = [];
self.adjacentMatrix = [];
//initialize the activated, in activation, previous activation
for(var i=0; i < totalNeuronCount; i++){
self.activated.push(false);
self.inActivation.push(false);
self.lastActivation.push(0);
//then we initialize our list of lists!
self.adjacentList.push([]);
self.reverseAdjacentList.push([]);
self.adjacentMatrix.push([]);
for(var j=0; j < totalNeuronCount; j++)
{
self.adjacentMatrix[i].push(0);
}
}
// console.log(self.adjacentList.length);
//finally
// Set up adjacency list and matrix
for (var i = 0; i < self.connections.length; i++)
{
var crs = self.connections[i].sourceIdx;
var crt = self.connections[i].targetIdx;
// Holds outgoing nodes
self.adjacentList[crs].push(crt);
// Holds incoming nodes
self.reverseAdjacentList[crt].push(crs);
self.adjacentMatrix[crs][crt] = connections[i].weight;
}
}
/// <summary>
/// Using RelaxNetwork erodes some of the perofrmance gain of FastConcurrentNetwork because of the slightly
/// more complex implemementation of the third loop - whe compared to SingleStep().
/// </summary>
/// <param name="maxSteps"></param>
/// <param name="maxAllowedSignalDelta"></param>
/// <returns></returns>
CPPN.prototype.relaxNetwork = function(maxSteps, maxAllowedSignalDelta)
{
var self = this;
var isRelaxed = false;
for (var j = 0; j < maxSteps && !isRelaxed; j++) {
isRelaxed = self.singleStepInternal(maxAllowedSignalDelta);
}
return isRelaxed;
};
CPPN.prototype.setInputSignal = function(index, signalValue)
{
var self = this;
// For speed we don't bother with bounds checks.
self.neuronSignals[self.biasNeuronCount + index] = signalValue;
};
CPPN.prototype.setInputSignals = function(signalArray)
{
var self = this;
// For speed we don't bother with bounds checks.
for (var i = 0; i < signalArray.length; i++)
self.neuronSignals[self.biasNeuronCount + i] = signalArray[i];
};
//we can dispense of this by accessing neuron signals directly
CPPN.prototype.getOutputSignal = function(index)
{
// For speed we don't bother with bounds checks.
return this.neuronSignals[this.totalInputNeuronCount + index];
};
//we can dispense of this by accessing neuron signals directly
CPPN.prototype.clearSignals = function()
{
var self = this;
// Clear signals for input, hidden and output nodes. Only the bias node is untouched.
for (var i = self.biasNeuronCount; i < self.neuronSignals.length; i++)
self.neuronSignals[i] = 0.0;
};
// cppn.CPPN.prototype.TotalNeuronCount = function(){ return this.neuronSignals.length;};
CPPN.prototype.recursiveActivation = function(){
var self = this;
// Initialize boolean arrays and set the last activation signal, but only if it isn't an input (these have already been set when the input is activated)
for (var i = 0; i < self.neuronSignals.length; i++)
{
// Set as activated if i is an input node, otherwise ensure it is unactivated (false)
self.activated[i] = (i < self.totalInputNeuronCount) ? true : false;
self.inActivation[i] = false;
if (i >= self.totalInputNeuronCount)
self.lastActivation[i] = self.neuronSignals[i];
}
// Get each output node activation recursively
// NOTE: This is an assumption that genomes have started minimally, and the output nodes lie sequentially after the input nodes
for (var i = 0; i < self.outputNeuronCount; i++)
self.recursiveActivateNode(self.totalInputNeuronCount + i);
};
CPPN.prototype.recursiveActivateNode = function(currentNode)
{
var self = this;
// If we've reached an input node we return since the signal is already set
if (self.activated[currentNode])
{
self.inActivation[currentNode] = false;
return;
}
// Mark that the node is currently being calculated
self.inActivation[currentNode] = true;
// Set the presignal to 0
self.neuronSignalsBeingProcessed[currentNode] = 0;
// Adjacency list in reverse holds incoming connections, go through each one and activate it
for (var i = 0; i < self.reverseAdjacentList[currentNode].length; i++)
{
var crntAdjNode = self.reverseAdjacentList[currentNode][i];
//{ Region recurrant connection handling - not applicable in our implementation
// If this node is currently being activated then we have reached a cycle, or recurrant connection. Use the previous activation in this case
if (self.inActivation[crntAdjNode])
{
//console.log('using last activation!');
self.neuronSignalsBeingProcessed[currentNode] += self.lastActivation[crntAdjNode]*self.adjacentMatrix[crntAdjNode][currentNode];
// parseFloat(
// parseFloat(self.lastActivation[crntAdjNode].toFixed(9)) * parseFloat(self.adjacentMatrix[crntAdjNode][currentNode].toFixed(9)).toFixed(9));
}
// Otherwise proceed as normal
else
{
// Recurse if this neuron has not been activated yet
if (!self.activated[crntAdjNode])
self.recursiveActivateNode(crntAdjNode);
// Add it to the new activation
self.neuronSignalsBeingProcessed[currentNode] += self.neuronSignals[crntAdjNode] *self.adjacentMatrix[crntAdjNode][currentNode];
// parseFloat(
// parseFloat(self.neuronSignals[crntAdjNode].toFixed(9)) * parseFloat(self.adjacentMatrix[crntAdjNode][currentNode].toFixed(9)).toFixed(9));
}
//} endregion
}
// Mark this neuron as completed
self.activated[currentNode] = true;
// This is no longer being calculated (for cycle detection)
self.inActivation[currentNode] = false;
// console.log('Current node: ' + currentNode);
// console.log('ActivationFunctions: ');
// console.log(self.activationFunctions);
//
// console.log('neuronSignals: ');
// console.log(self.neuronSignals);
//
// console.log('neuronSignalsBeingProcessed: ');
// console.log(self.neuronSignalsBeingProcessed);
// Set this signal after running it through the activation function
self.neuronSignals[currentNode] = self.activationFunctions[currentNode].calculate(self.neuronSignalsBeingProcessed[currentNode]);
// parseFloat((self.activationFunctions[currentNode].calculate(parseFloat(self.neuronSignalsBeingProcessed[currentNode].toFixed(9)))).toFixed(9));
};
CPPN.prototype.isRecursive = function()
{
var self = this;
//if we're a hidden/output node (nodeid >= totalInputcount), and we connect to an input node (nodeid <= self.totalInputcount) -- it's recurrent!
//if we are a self connection, duh we are recurrent
for(var c=0; c< self.connections.length; c++)
if((self.connections[c].sourceIdx >= self.totalInputNeuronCount
&& self.connections[c].targetIdx < self.totalInputNeuronCount)
|| self.connections[c].sourceIdx == self.connections[c].targetIdx
)
return true;
self.recursed = [];
self.inRecursiveCheck = [];
for(var i=0; i < self.neuronSignals.length; i++)
{
self.recursed.push((i < self.totalInputNeuronCount) ? true : false);
self.inRecursiveCheck.push(false);
}
// Get each output node activation recursively
// NOTE: This is an assumption that genomes have started minimally, and the output nodes lie sequentially after the input nodes
for (var i = 0; i < self.outputNeuronCount; i++){
if(self.recursiveCheckRecursive(self.totalInputNeuronCount + i))
{
// console.log('Returned one!');
return true;
}
}
return false;
};
CPPN.prototype.recursiveCheckRecursive = function(currentNode)
{
var self = this;
// console.log('Self recursed : '+ currentNode + ' ? ' + self.recursed[currentNode]);
// console.log('Checking: ' + currentNode)
// If we've reached an input node we return since the signal is already set
if (self.recursed[currentNode])
{
self.inRecursiveCheck[currentNode] = false;
return false;
}
// Mark that the node is currently being calculated
self.inRecursiveCheck[currentNode] = true;
// Adjacency list in reverse holds incoming connections, go through each one and activate it
for (var i = 0; i < self.reverseAdjacentList[currentNode].length; i++)
{
var crntAdjNode = self.reverseAdjacentList[currentNode][i];
//{ Region recurrant connection handling - not applicable in our implementation
// If this node is currently being activated then we have reached a cycle, or recurrant connection. Use the previous activation in this case
if (self.inRecursiveCheck[crntAdjNode])
{
self.inRecursiveCheck[currentNode] = false;
return true;
}
// Otherwise proceed as normal
else
{
var verifiedRecursive;
// Recurse if this neuron has not been activated yet
if (!self.recursed[crntAdjNode])
verifiedRecursive = self.recursiveCheckRecursive(crntAdjNode);
if(verifiedRecursive)
return true;
}
//} endregion
}
// Mark this neuron as completed
self.recursed[currentNode] = true;
// This is no longer being calculated (for cycle detection)
self.inRecursiveCheck[currentNode] = false;
return false;
};
(function(exports, selfBrowser, isBrowser){
var cppn = {CPPN: {}};
//send in the object, and also whetehr or not this is nodejs
})(typeof exports === 'undefined'? this['cppnjs']['cppn']={}: exports, this, typeof exports === 'undefined'? true : false);
/**
* Module dependencies.
*/
//none
/**
* Expose `cppnConnection`.
*/
module.exports = cppnConnection;
/**
* Initialize a new cppnConnection.
*
* @param {Number} sourceIdx
* @param {Number} targetIdx
* @param {Number} cWeight
* @api public
*/
//simple connection type -- from FloatFastConnection.cs
function cppnConnection(
sourceIdx,
targetIdx,
cWeight
){
var self = this;
self.sourceIdx = sourceIdx;
self.targetIdx = targetIdx;
self.weight = cWeight;
self.signal =0;
}
/**
* Module dependencies.
*/
var NodeType = require("../types/nodeType");
/**
* Expose `cppnNode`.
*/
module.exports = cppnNode;
/**
* Initialize a new cppnNode.
*
* @param {String} actFn
* @param {String} neurType
* @param {String} nid
* @api public
*/
function cppnNode(actFn, neurType, nid){
var self = this;
self.neuronType = neurType;
self.id = nid;
self.outputValue = (self.neuronType == NodeType.bias ? 1.0 : 0.0);
self.activationFunction = actFn;
}
var NodeType =
{
bias : "Bias",
input: "Input",
output: "Output",
hidden: "Hidden",
other : "Other"
};
module.exports = NodeType;
var utils = {};
module.exports = utils;
utils.stringToFunction = function(str) {
var arr = str.split(".");
var fn = (window || this);
for (var i = 0, len = arr.length; i < len; i++) {
fn = fn[arr[i]];
}
if (typeof fn !== "function") {
throw new Error("function not found");
}
return fn;
};
utils.nextDouble = function()
{
return Math.random();
};
utils.next = function(range)
{
return Math.floor((Math.random()*range));
};
utils.tanh = function(arg) {
// sinh(number)/cosh(number)
return (Math.exp(arg) - Math.exp(-arg)) / (Math.exp(arg) + Math.exp(-arg));
};
utils.sign = function(input)
{
if (input < 0) {return -1;}
if (input > 0) {return 1;}
return 0;
};
//ROULETTE WHEEL class
//if we need a node object, this is how we would do it
// var neatNode = isNodejs ? self['neatNode'] : require('./neatNode.js');
utils.RouletteWheel =
{
};
/// <summary>
/// A simple single throw routine.
/// </summary>
/// <param name="probability">A probability between 0..1 that the throw will result in a true result.</param>
/// <returns></returns>
utils.RouletteWheel.singleThrow = function(probability)
{
return (utils.nextDouble() <= probability);
};
/// <summary>
/// Performs a single throw for a given number of outcomes with equal probabilities.
/// </summary>
/// <param name="numberOfOutcomes"></param>
/// <returns>An integer between 0..numberOfOutcomes-1. In effect this routine selects one of the possible outcomes.</returns>
utils.RouletteWheel.singleThrowEven = function(numberOfOutcomes)
{
var probability= 1.0 / numberOfOutcomes;
var accumulator=0;
var throwValue = utils.nextDouble();
for(var i=0; i<numberOfOutcomes; i++)
{
accumulator+=probability;
if(throwValue<=accumulator)
return i;
}
//throw exception in javascript
throw "PeannutLib.Maths.SingleThrowEven() - invalid outcome.";
};
/// <summary>
/// Performs a single thrown onto a roulette wheel where the wheel's space is unevenly divided.
/// The probabilty that a segment will be selected is given by that segment's value in the 'probabilities'
/// array. The probabilities are normalised before tossing the ball so that their total is always equal to 1.0.
/// </summary>
/// <param name="probabilities"></param>
/// <returns></returns>
utils.RouletteWheel.singleThrowArray = function(aProbabilities)
{
if(typeof aProbabilities === 'number')
throw new Error("Send Array to singleThrowArray!");
var pTotal=0; // Total probability
//-----
for(var i=0; i<aProbabilities.length; i++)
pTotal+= aProbabilities[i];
//----- Now throw the ball and return an integer indicating the outcome.
var throwValue = utils.nextDouble() * pTotal;
var accumulator=0;
for(var j=0; j< aProbabilities.length; j++)
{
accumulator+= aProbabilities[j];
if(throwValue<=accumulator)
return j;
}
throw "PeannutLib.Maths.singleThrowArray() - invalid outcome.";
};
/// <summary>
/// Similar in functionality to SingleThrow(double[] probabilities). However the 'probabilities' array is
/// not normalised. Therefore if the total goes beyond 1 then we allow extra throws, thus if the total is 10
/// then we perform 10 throws.
/// </summary>
/// <param name="probabilities"></param>
/// <returns></returns>
utils.RouletteWheel.multipleThrows = function(aProbabilities)
{
var pTotal=0; // Total probability
var numberOfThrows;
//----- Determine how many throws of the ball onto the wheel.
for(var i=0; i<aProbabilities.length; i++)
pTotal+=aProbabilities[i];
// If total probabilty is > 1 then we take this as meaning more than one throw of the ball.
var pTotalInteger = Math.floor(pTotal);
var pTotalRemainder = pTotal - pTotalInteger;
numberOfThrows = Math.floor(pTotalInteger);
if(utils.nextDouble() <= pTotalRemainder)
numberOfThrows++;
//----- Now throw the ball the determined number of times. For each throw store an integer indicating the outcome.
var outcomes = [];//new int[numberOfThrows];
for(var a=0; a < numberOfThrows; a++)
outcomes.push(0);
for(var i=0; i<numberOfThrows; i++)
{
var throwValue = utils.nextDouble() * pTotal;
var accumulator=0;
for(var j=0; j<aProbabilities.length; j++)
{
accumulator+=aProbabilities[j];
if(throwValue<=accumulator)
{
outcomes[i] = j;
break;
}
}
}
return outcomes;
};
utils.RouletteWheel.selectXFromSmallObject = function(x, objects){
var ixs = [];
//works with objects with count or arrays with length
var gCount = objects.count === undefined ? objects.length : objects.count;
for(var i=0; i<gCount;i++)
ixs.push(i);
//how many do we need back? we need x back. So we must remove (# of objects - x) leaving ... x objects
for(var i=0; i < gCount -x; i++)
{
//remove random index
ixs.splice(utils.next(ixs.length),1);
}
return ixs;
};
utils.RouletteWheel.selectXFromLargeObject = function(x, objects)
{
var ixs = [];
var guesses = {};
var gCount = objects.count === undefined ? objects.length : objects.count;
//we make sure the number of requested objects is less than the object indices
x = Math.min(x, gCount);
for(var i=0; i<x; i++)
{
var guessIx = utils.next(gCount);
while(guesses[guessIx])
guessIx = utils.next(gCount);
guesses[guessIx] = true;
ixs.push(guessIx);
}
return ixs;
};

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display