Browse Source

Added comments to GA algo

pull/18/head
jrtechs 5 years ago
parent
commit
b92ba144f3
1 changed files with 121 additions and 29 deletions
  1. +121
    -29
      geneticAlgorithm/geneticAlgo.html

+ 121
- 29
geneticAlgorithm/geneticAlgo.html View File

@ -12,6 +12,12 @@
<script> <script>
class Gene class Gene
{ {
/**
* Constructs a new Gene to store in a chromosome.
* @param min minimum value that this gene can store
* @param max value this gene can possibly be
* @param value normalized value
*/
constructor(min, max, value) constructor(min, max, value)
{ {
this.min = min; this.min = min;
@ -19,6 +25,10 @@
this.value = value; this.value = value;
} }
/**
* De-normalizes the value of the gene
* @returns {*}
*/
getRealValue() getRealValue()
{ {
return (this.max - this.min) * this.value + this.min; return (this.max - this.min) * this.value + this.min;
@ -48,6 +58,11 @@
class Chromosome class Chromosome
{ {
/**
* Constructs a chromosome by making a copy of
* a list of genes.
* @param geneArray
*/
constructor(geneArray) constructor(geneArray)
{ {
this.genes = []; this.genes = [];
@ -62,11 +77,20 @@
return this.genes; return this.genes;
} }
/**
* Mutates a random gene.
*/
mutate() mutate()
{ {
this.genes[Math.round(Math.random() * (this.genes.length-1))].setValue(Math.random()); this.genes[Math.round(Math.random() * (this.genes.length-1))].setValue(Math.random());
} }
/**
* Creates a totally new chromosome with same
* genetic structure as this chromosome but different
* values.
* @returns {Chromosome}
*/
createRandomChromosome() createRandomChromosome()
{ {
let geneAr = []; let geneAr = [];
@ -79,6 +103,13 @@
} }
/**
* Mates two chromosomes using the blending method
* and returns a list of 2 offspring.
* @param father
* @param mother
* @returns {Chromosome[]}
*/
const breed = function(father, mother) const breed = function(father, mother)
{ {
let son = new Chromosome(father.getGenes()); let son = new Chromosome(father.getGenes());
@ -89,10 +120,31 @@
let blendCoef = Math.random(); let blendCoef = Math.random();
blendGene(son.getGenes()[i], daughter.getGenes()[i], blendCoef); blendGene(son.getGenes()[i], daughter.getGenes()[i], blendCoef);
} }
return [son, daughter]; return [son, daughter];
}; };
/**
* Blends two genes together based on a random blend
* coefficient.
**/
const blendGene = function(gene1, gene2, blendCoef)
{
let value1 = (blendCoef * gene1.getValue()) +
(gene2.getValue() * (1- blendCoef));
let value2 = ((1-blendCoef) * gene1.getValue()) +
(gene2.getValue() * blendCoef);
gene1.setValue(value1);
gene2.setValue(value2);
};
/**
* Helper function to sort an array
*
* @param prop name of JSON property to sort by
* @returns {function(*, *): number}
*/
function predicateBy(prop) function predicateBy(prop)
{ {
return function(a,b) return function(a,b)
@ -110,7 +162,17 @@
} }
} }
/**
* Function which computes the fitness of everyone in the
* population and returns the most fit survivors. Method
* known as elitism.
*
* @param population
* @param keepNumber
* @param fitnessFunction
* @returns {{average: number,
* survivors: Array, bestFit: Chromosome }}
*/
const naturalSelection = function(population, keepNumber, fitnessFunction) const naturalSelection = function(population, keepNumber, fitnessFunction)
{ {
let fitnessArray = []; let fitnessArray = [];
@ -135,22 +197,17 @@
return {average: total/population.length, survivors: survivors, bestFit: bestFitness, bestChrom: bestChromosome}; return {average: total/population.length, survivors: survivors, bestFit: bestFitness, bestChrom: bestChromosome};
}; };
const blendGene = function(gene1, gene2, blendCoef)
{
let value1 = (blendCoef * gene1.getValue()) +
(gene2.getValue() * (1- blendCoef));
let value2 = ((1-blendCoef) * gene1.getValue()) +
(gene2.getValue() * blendCoef);
gene1.setValue(value1);
gene2.setValue(value2);
};
/**
* Randomly everyone in the population
*
* @param population
* @param desiredPopulationSize
*/
const matePopulation = function(population, desiredPopulationSize) const matePopulation = function(population, desiredPopulationSize)
{ {
let pairsNeeded = (desiredPopulationSize - population.length)/2;
const originalLength = population.length; const originalLength = population.length;
for(let i = 0; i < pairsNeeded; i++)
while(population.length < desiredPopulationSize)
{ {
let index1 = Math.round(Math.random() * (originalLength-1)); let index1 = Math.round(Math.random() * (originalLength-1));
let index2 = Math.round(Math.random() * (originalLength-1)); let index2 = Math.round(Math.random() * (originalLength-1));
@ -163,7 +220,9 @@
} }
}; };
/**
* Randomly mutates the population
**/
const mutatePopulation = function(population, mutatePercentage) const mutatePopulation = function(population, mutatePercentage)
{ {
if(population.length >= 2) if(population.length >= 2)
@ -182,6 +241,11 @@
} }
}; };
/**
* Introduces x random chromosomes to the population.
* @param population
* @param immigrationSize
*/
const newBlood = function(population, immigrationSize) const newBlood = function(population, immigrationSize)
{ {
for(let i = 0; i < immigrationSize; i++) for(let i = 0; i < immigrationSize; i++)
@ -192,15 +256,28 @@
}; };
let costx = Math.random() * 10;
let costy = Math.random() * 10;
/** Defines the cost as the "distance" to a 2-d point.
* @param chromosome
* @returns {number}
*/
const basicCostFunction = function(chromosome) const basicCostFunction = function(chromosome)
{ {
console.log(chromosome);
console.log((chromosome.getGenes()[0].getRealValue()));
return Math.abs(chromosome.getGenes()[0].getRealValue() - 6) +
Math.abs(chromosome.getGenes()[1].getRealValue() - 2);
return Math.abs(chromosome.getGenes()[0].getRealValue() - costx) +
Math.abs(chromosome.getGenes()[1].getRealValue() - costy);
}; };
/**
* Creates a totally random population based on a desired size
* and a prototypical chromosome.
*
* @param geneticChromosome
* @param populationSize
* @returns {Array}
*/
const createRandomPopulation = function(geneticChromosome, populationSize) const createRandomPopulation = function(geneticChromosome, populationSize)
{ {
let population = []; let population = [];
@ -212,18 +289,34 @@
}; };
/**
* Runs the genetic algorithm by going through the processes of
* natural selection, mutation, mating, and immigrations. This
* process will continue until an adequately performing chromosome
* is found or a generation threshold is passed.
*
* @param geneticChromosome Prototypical chromosome: used so algo knows
* what the dna of the population looks like.
* @param costFunction Function which defines how bad a Chromosome is
* @param populationSize Desired population size for population
* @param maxGenerations Cut off level for number of generations to run
* @param desiredCost Sufficient cost to terminate program at
* @param mutationRate Number between [0,1] representing proportion of genes
* to mutate each generation
* @param keepNumber Number of Organisms which survive each generation
* @param newBloodNumber Number of random immigrants to introduce into
* the population each generation.
* @returns {*}
*/
const runGeneticOptimization = function(geneticChromosome, costFunction, const runGeneticOptimization = function(geneticChromosome, costFunction,
populationSize, maxGenerations, populationSize, maxGenerations,
desiredCost, mutationRate, keepNumber, desiredCost, mutationRate, keepNumber,
newBloodNumber) newBloodNumber)
{ {
let population = createRandomPopulation(geneticChromosome, populationSize); let population = createRandomPopulation(geneticChromosome, populationSize);
let generation = 0; let generation = 0;
let bestCost = Number.MAX_VALUE; let bestCost = Number.MAX_VALUE;
let bestChromosome = geneticChromosome; let bestChromosome = geneticChromosome;
do do
{ {
matePopulation(population, populationSize); matePopulation(population, populationSize);
@ -240,21 +333,20 @@
generation++; generation++;
console.log("Generation " + generation + " Best Cost: " + bestCost); console.log("Generation " + generation + " Best Cost: " + bestCost);
console.log(generationResult);
}while(generation < maxGenerations && bestCost > desiredCost); }while(generation < maxGenerations && bestCost > desiredCost);
return bestChromosome; return bestChromosome;
}; };
/**
* Ugly globals used to keep track of population state for the graph.
*/
let genericChromosomeG, costFunctionG, let genericChromosomeG, costFunctionG,
populationSizeG, maxGenerationsG, populationSizeG, maxGenerationsG,
desiredCostG, mutationRateG, keepNumberG, desiredCostG, mutationRateG, keepNumberG,
newBloodNumberG, populationG, generationG, newBloodNumberG, populationG, generationG,
bestCostG = Number.MAX_VALUE, bestChromosomeG = genericChromosomeG; bestCostG = Number.MAX_VALUE, bestChromosomeG = genericChromosomeG;
const runGeneticOptimizationforGraph = function()
const runGeneticOptimizationForGraph = function()
{ {
let generationResult = naturalSelection(populationG, keepNumberG, costFunctionG); let generationResult = naturalSelection(populationG, keepNumberG, costFunctionG);
@ -416,7 +508,7 @@
{ {
if(autoRunning === true) if(autoRunning === true)
{ {
runGeneticOptimizationforGraph();
runGeneticOptimizationForGraph();
setTimeout(runAutoOptimizer, 1000); setTimeout(runAutoOptimizer, 1000);
} }
} }
@ -449,7 +541,7 @@
<div id="line_chart"></div> <div id="line_chart"></div>
<input class='btn btn-primary' id="runOptimizer" onclick='runGeneticOptimizationforGraph()' type="button" value="Next Generation">
<input class='btn btn-primary' id="runOptimizer" onclick='runGeneticOptimizationForGraph()' type="button" value="Next Generation">
<input class='btn btn-primary' id="runAutoOptimizer" onclick='startStopAutoRun()' type="button" value="Auto Run"> <input class='btn btn-primary' id="runAutoOptimizer" onclick='startStopAutoRun()' type="button" value="Auto Run">
<div class="card"> <div class="card">

Loading…
Cancel
Save