Created
February 27, 2014 18:43
-
-
Save adrianseeley/9256385 to your computer and use it in GitHub Desktop.
GATO.PLINKO
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function Plinko () { | |
var Ref = { | |
Train: function (Training_Set, Train_Till_Errors, Train_Till_Iterations, Max_Medium_Population, Nerf_Rate) { | |
// Define processes | |
var Processes = [ | |
function Process_Identity (Self) { | |
// Distribute all inputs to all outputs | |
for (var a = 0; a < Self.Outputs.length; a++) for (var b = 0; b < Self.Inputs.length; b++) Ref.Medium[Self.Outputs[a].Layer][Self.Outputs[a].Node].Inputs.push(Self.Inputs[b]); | |
}, | |
function Process_Summation (Self) { | |
// Distribute sum of all inputs to all outputs | |
var Sum = 0; for (var a = 0; a < Self.Inputs.length; a++) Sum += Self.Inputs[a]; | |
for (var a = 0; a < Self.Outputs.length; a++) Ref.Medium[Self.Outputs[a].Layer][Self.Outputs[a].Node].Inputs.push(Sum); | |
, | |
function Process_Average (Self) { | |
// Distribute average of all inputs to all outputs | |
var Average = 0; for (var a = 0; a < Self.Inputs.length; a++) Average += Self.Inputs[a]; Average /= Self.Inputs.length; | |
for (var a = 0; a < Self.Outputs.length; a++) Ref.Medium[Self.Outputs[a].Layer][Self.Outputs[a].Node].Inputs.push(Average); | |
} | |
]; | |
// Define training set evaluator | |
function Evaluate_Training_Cases (Medium, Training_Set) { | |
// Calculate class output pairs for training set | |
var Class_Output_Pairs = []; for (var a = 0; a < Training_Set.length; a++) Class_Output_Pairs.push({Class: Training_Set[a].Output, Output: Evaluate_Medium(Medium, Training_Set[a].Inputs)}); | |
return Class_Output_Pairs; | |
}; | |
// Define medium evaluation function | |
function Evaluate_Medium (Medium, Inputs) { | |
// Reset medium | |
for (var a = 0; a < Medium.length; a++) for (var b = 0; b < Medium[a].length; b++) for (var c = 0; c < Medium[a][b].Inputs.length; c++) Medium[a][b].Inputs[c] = []; | |
// Stimulate medium with inputs | |
for (var a = 0; a < Inputs.length; a++) Medium[0][a].Inputs.push(Inputs[a]); | |
// Process medium (note last layer is summation layer and is skipped) | |
for (var a = 0; a < Medium.length - 1; a++) for (var b = 0; b < Medium[a].length; b++) Medium[a][b].Process(Medium[a][b]); | |
// Calculate the summation of the output layer | |
var Sum = 0; for (var a = 0; a < Medium[Medium.length - 1][0].Inputs.length; a++) Sum += Medium[Medium.length - 1][0].Inputs[a]; | |
// Return evaluated sum | |
return Sum; | |
}; | |
// Define fitness function | |
function Fitness (Class_Output_Pairs) { | |
// Calculate the divergence and convergence of all pairs | |
var Divergence = 0; var Convergence = 0; | |
for (var a = 0; a < Class_Output_Pairs.length; a++) for (var b = 0; b < Class_Output_Pairs.length; b++) { | |
if (a == b) continue; | |
if (Class_Output_Pairs[a].Class == Class_Output_Pairs[b].Class) Convergence += Math.abs(Class_Output_Pairs[b].Output - Class_Output_Pairs[a].Output); | |
else Divergence += Math.abs(Class_Output_Pairs[b].Output - Class_Output_Pairs[a].Output); | |
} | |
// Return the divergence convergence ratio as the fitness | |
return Convergence / Divergence; | |
}; | |
// Define medium mutation function | |
function Mutate (Medium) { | |
// First duplicate the medium | |
var Mutant = JSON.parse(JSON.stringify(Medium)); | |
// There are 6 types of mutation, collect them all | |
var Available_Mutations = []; | |
// 1. Remove any genetic node, and possibly a genetic row if it is the last node in that row | |
for (var a = 1; a < Medium.length - 1; a++) for (var b = 0; b < Medium[a].length; b++) Available_Mutations.push({Type: 'Remove Genetic Node', Layer: a, Node: b}); | |
// 2. Add a genetic node to an existing genetic row | |
for (var a = 1; a < Medium.length - 1; a++) Available_Mutations.push({Type: 'Add Genetic Node', Layer: a}); | |
// 3. Shuffle the process of any input or genetic node | |
for (var a = 1; a < Medium.length; a++) for (var b = 0; b < Medium[a].length; b++) Available_Mutations.push({Type: 'Shuffle Genetic Node Process', Layer: a, Node: b}); | |
// 4. Add an additional output to any genetic or input node | |
for (var a = 0; a < Medium.length - 1; a++) for (var b = 0; b < Medium[a].length; b++) for (var c = a + 1; c < Medium.length; c++) for (var d = 0; d < Medium[c].length; d++) Available_Mutations.push({Type: 'Add Input Or Genetic Node Output', Layer: a, Node: b, Output_Layer: c, Output_Node: d}); | |
// 5. Remove an output from any genetic or input node, cannot remove the last output from a node | |
for (var a = 0; a < Medium.length - 1; a++) for (var b = 0; b < Medium[a].length; b++) if (Medium[a][b].Outputs.length > 1) for (var c = 0; c < Medium[a][b].Outputs.length; c++) Available_Mutations.push({Type: 'Remove Input Or Genetic Node Output', Layer: a, Node: b, Remove_Output_Index: c}); | |
// 6. Add a new genetic row with a new genetic node in it, has no preconditions | |
for (var a = 1; a < Medium.length; a++) Available_Mutations.push({Type: 'Add Genetic Row', Layer: a}); | |
// Choose one of the available mutations | |
var Mutation = Available_Mutations[Math.floor(Math.random() * Available_Mutations.length)]; | |
// Branch by mutation type | |
switch (Mutation.Type) { | |
case 'Remove Genetic Node': | |
// Ensure any other node pointing at this node gains the outputs of the removed genetic node in it's place | |
for (var a = 0; a < Medium.length - 1; a++) for (var b = 0; b < Medium[a].length; b++) for (var c = 0; c < Medium[a][b].Outputs.length; c++) if (Medium[a][b].Outputs[c].Layer == Mutation.Layer && Medium[a][b].Outputs[c].Node == Mutation.Node) Medium[a][b].Outputs = Medium[a][b].slice(0, c).concat(Medium[Mutation.Layer][Mutation.Node].Outputs).concat(Medium[a][b].slice(c + 1)); | |
// Ensure any other node pointing at the genetic layer of the mutation but at a later genetic node has it's node value decremented to match the removal of a node in front of it | |
for (var a = 0; a < Medium.length - 1; a++) for (var b = 0; b < Medium[a].length; b++) for (var c = 0; c < Medium[a][b].Outputs.length; c++) if (Medium[a][b].Outputs[c].Layer == Mutation.Layer && Medium[a][b].Outputs[c].Node > Mutation.Node) Medium[a][b].Outputs[c].Node--; | |
// Now that this genetic node has no inputs, and we shifted any pointers that were affected, remove node | |
Medium[Mutation.Layer].splice(Mutation.Node, 1); | |
// If genetic row has been left empty, it must be removed | |
if (Medium[Mutation.Layer].length == 0) { | |
// Decrement any row pointers after the row to be removed to accomodate the removed row | |
for (var a = 0; a < Medium.length - 1; a++) for (var b = 0; b < Medium[a].length; b++) for (var c = 0; c < Medium[a][b].Outputs.length; c++) if (Medium[a][b].Outputs[c].Layer > Mutation.Layer) Medium[a][b].Outputs[c].Layer--; | |
// Remove empty genetic row | |
Medium.splice(Mutation.Layer, 1); | |
} | |
break; | |
case 'Add Genetic Node': | |
// Genetic nodes always get added at the end of the layer to avoid pointer breaking | |
Medium[Mutation.Layer].push({Inputs: [], Outputs: [{Layer: Mutation.Layer + 1, Node: 0}], Process: Math.floor(Math.random() * Processes.length)}); | |
break; | |
case 'Shuffle Genetic Node Process': | |
// Shuffle the input or genetic nodes process | |
Medium[Mutation.Layer][Mutation.Node].Process = Math.floor(Math.random() * Processes.length); | |
break; | |
case 'Add Input Or Genetic Node Output': | |
// Add the input or genetic node output | |
Medium[Mutation.Layer][Mutation.Node].Outputs.push({Layer: Mutation.Output_Layer, Node: Mutation.Output_Node}); | |
break; | |
case 'Remove Input Or Genetic Node Output': | |
// Remove the input or genetic node output by index | |
Medium[Mutation.Layer][Mutation.Node].Outputs.splice(Mutation.Remove_Output_Index, 1); | |
break; | |
case 'Add Genetic Row': | |
// Add a new genetic row with a new genetic node in it | |
Medium.splice(Mutation.Layer, 0, [{Inputs: [], Outputs: [{Layer: Mutation.Layer + 1, Node: 0}], Process: Math.floor(Math.random() * Processes.length)}]); | |
break; | |
default: throw 'Unknown Mutation Type: ' + JSON.stringify(Mutation); | |
} | |
// Return mutant | |
return Mutant; | |
}; | |
// Create medium population with a single medium | |
var Medium_Population = [[]]; | |
// Create input layer in starting medium population | |
Medium_Population[0].push([]); | |
for (var a = 0; a < Training_Set[0].Inputs.length; a++) Medium_Population[0][0].push({Inputs: [], Outputs: [{Layer: 1, Node: 0}], Process: 0}); | |
// Create output layer in starting medium population | |
Medium_Population[0].push([{Inputs: []}]); | |
// Measure fitness of starting medium population | |
Medium_Population[0].Fitness = Fitness(Evaluate_Training_Cases(Medium_Population[0], Training_Set)); | |
// Provide a nerf to the starting medium population | |
Medium_Population[0].Nerf = 1.0; | |
// Mutation loop | |
for (var a = 0; a < Train_Till_Iterations && Medium_Population[0].Fitness > Train_Till_Errors; a++) { | |
// Mutate the queen and add it to the population | |
Medium_Population.push(Mutate(Medium_Population[0])); | |
// Measure fitness of mutant | |
Medium_Population[Medium_Population.length - 1].Fitness = Fitness(Evaluate_Training_Cases(Medium_Population[Medium_Population.length - 1], Training_Set)); | |
// Provide a nerf to mutant | |
Medium_Population[Medium_Population.length - 1].Nerf = 1.0; | |
// If queen failed to produce a more fit mutant, nerf queen | |
if (Medium_Population[Medium_Population.length - 1].Fitness >= Medium_Population[0].Fitness) Medium_Population[0].Nerf *= Nerf_Rate; | |
// Sort medium population by fitness * nerf | |
Medium_Population.sort(function (Left_Medium, Right_Medium) { return (Left_Medium.Fitness * Left_Medium.Nerf) - (Right_Medium.Fitness * Right_Medium.Nerf); }); | |
// Cull any overpopulation | |
while (Medium_Population.length > Max_Medium_Population) Medium_Population.pop(); | |
} | |
} | |
}; | |
return Ref; | |
}; | |
var p = Plinko(); | |
console.log(p); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment