<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
							 | 
						|
								<script
							 | 
						|
								        src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
							 | 
						|
								        integrity="sha256-3edrmyuQ0w65f8gfBsqowzjJe2iM6n0nKciPUp8y+7E="
							 | 
						|
								        crossorigin="anonymous">
							 | 
						|
								</script>
							 | 
						|
								<script>
							 | 
						|
								    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)
							 | 
						|
								        {
							 | 
						|
								            this.min = min;
							 | 
						|
								            this.max = max;
							 | 
						|
								            this.value = value;
							 | 
						|
								        }
							 | 
						|
								        /**
							 | 
						|
								         * De-normalizes the value of the gene
							 | 
						|
								         * @returns {*}
							 | 
						|
								         */
							 | 
						|
								        getRealValue()
							 | 
						|
								        {
							 | 
						|
								            return (this.max - this.min) * this.value + this.min;
							 | 
						|
								        }
							 | 
						|
								        getValue()
							 | 
						|
								        {
							 | 
						|
								            return this.value;
							 | 
						|
								        }
							 | 
						|
								        setValue(val)
							 | 
						|
								        {
							 | 
						|
								            this.value = val;
							 | 
						|
								        }
							 | 
						|
								        makeClone()
							 | 
						|
								        {
							 | 
						|
								            return new Gene(this.min, this.max, this.value);
							 | 
						|
								        }
							 | 
						|
								        makeRandomGene()
							 | 
						|
								        {
							 | 
						|
								            return new Gene(this.min, this.max, Math.random());
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								    class Chromosome
							 | 
						|
								    {
							 | 
						|
								        /**
							 | 
						|
								         * Constructs a chromosome by making a copy of
							 | 
						|
								         * a list of genes.
							 | 
						|
								         * @param geneArray
							 | 
						|
								         */
							 | 
						|
								        constructor(geneArray)
							 | 
						|
								        {
							 | 
						|
								            this.genes = [];
							 | 
						|
								            for(let i = 0; i < geneArray.length; i++)
							 | 
						|
								            {
							 | 
						|
								                this.genes.push(geneArray[i].makeClone());
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								        getGenes()
							 | 
						|
								        {
							 | 
						|
								            return this.genes;
							 | 
						|
								        }
							 | 
						|
								        /**
							 | 
						|
								         * Mutates a random gene.
							 | 
						|
								         */
							 | 
						|
								        mutate()
							 | 
						|
								        {
							 | 
						|
								            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()
							 | 
						|
								        {
							 | 
						|
								            let geneAr = [];
							 | 
						|
								            for(let i = 0; i < this.genes.length; i++)
							 | 
						|
								            {
							 | 
						|
								                geneAr.push(this.genes[i].makeRandomGene());
							 | 
						|
								            }
							 | 
						|
								            return new Chromosome(geneAr);
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								    /**
							 | 
						|
								     * 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)
							 | 
						|
								    {
							 | 
						|
								        let son = new Chromosome(father.getGenes());
							 | 
						|
								        let daughter = new Chromosome(mother.getGenes());
							 | 
						|
								        for(let i = 0;i < son.getGenes().length; i++)
							 | 
						|
								        {
							 | 
						|
								            let blendCoef = Math.random();
							 | 
						|
								            blendGene(son.getGenes()[i], daughter.getGenes()[i], blendCoef);
							 | 
						|
								        }
							 | 
						|
								        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)
							 | 
						|
								    {
							 | 
						|
								        return function(a,b)
							 | 
						|
								        {
							 | 
						|
								            var result;
							 | 
						|
								            if(a[prop] > b[prop])
							 | 
						|
								            {
							 | 
						|
								                result =  1;
							 | 
						|
								            }
							 | 
						|
								            else if(a[prop] < b[prop])
							 | 
						|
								            {
							 | 
						|
								                result = -1;
							 | 
						|
								            }
							 | 
						|
								            return result;
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								    /**
							 | 
						|
								     * 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)
							 | 
						|
								    {
							 | 
						|
								        let fitnessArray = [];
							 | 
						|
								        let total = 0;
							 | 
						|
								        for(let i = 0; i < population.length; i++)
							 | 
						|
								        {
							 | 
						|
								            const fitness = fitnessFunction(population[i]);
							 | 
						|
								            fitnessArray.push({fit:fitness, chrom: population[i]});
							 | 
						|
								            total+= fitness;
							 | 
						|
								        }
							 | 
						|
								        fitnessArray.sort(predicateBy("fit"));
							 | 
						|
								        let survivors = [];
							 | 
						|
								        let bestFitness = fitnessArray[0].fit;
							 | 
						|
								        let bestChromosome = fitnessArray[0].chrom;
							 | 
						|
								        for(let i = 0; i < keepNumber; i++)
							 | 
						|
								        {
							 | 
						|
								            survivors.push(fitnessArray[i].chrom);
							 | 
						|
								        }
							 | 
						|
								        return {average: total/population.length, survivors: survivors, bestFit: bestFitness, bestChrom: bestChromosome};
							 | 
						|
								    };
							 | 
						|
								    /**
							 | 
						|
								     * Randomly  everyone in the population
							 | 
						|
								     *
							 | 
						|
								     * @param population
							 | 
						|
								     * @param desiredPopulationSize
							 | 
						|
								     */
							 | 
						|
								    const matePopulation = function(population, desiredPopulationSize)
							 | 
						|
								    {
							 | 
						|
								        const originalLength = population.length;
							 | 
						|
								        while(population.length < desiredPopulationSize)
							 | 
						|
								        {
							 | 
						|
								            let index1 = Math.round(Math.random() * (originalLength-1));
							 | 
						|
								            let index2 = Math.round(Math.random() * (originalLength-1));
							 | 
						|
								            if(index1 !== index2)
							 | 
						|
								            {
							 | 
						|
								                const babies = breed(population[index1], population[index2]);
							 | 
						|
								                population.push(babies[0]);
							 | 
						|
								                population.push(babies[1]);
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								    };
							 | 
						|
								    /**
							 | 
						|
								     * Randomly mutates the population
							 | 
						|
								     **/
							 | 
						|
								    const mutatePopulation = function(population, mutatePercentage)
							 | 
						|
								    {
							 | 
						|
								        if(population.length >= 2)
							 | 
						|
								        {
							 | 
						|
								            let mutations = mutatePercentage *
							 | 
						|
								                population.length *
							 | 
						|
								                population[0].getGenes().length;
							 | 
						|
								            for(let i = 0; i < mutations; i++)
							 | 
						|
								            {
							 | 
						|
								                population[i].mutate();
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								        else
							 | 
						|
								        {
							 | 
						|
								            console.log("Error, population too small to mutate");
							 | 
						|
								        }
							 | 
						|
								    };
							 | 
						|
								    /**
							 | 
						|
								     * Introduces x random chromosomes to the population.
							 | 
						|
								     * @param population
							 | 
						|
								     * @param immigrationSize
							 | 
						|
								     */
							 | 
						|
								    const newBlood = function(population, immigrationSize)
							 | 
						|
								    {
							 | 
						|
								        for(let i = 0; i < immigrationSize; i++)
							 | 
						|
								        {
							 | 
						|
								            let geneticChromosome = population[0];
							 | 
						|
								            population.push(geneticChromosome.createRandomChromosome());
							 | 
						|
								        }
							 | 
						|
								    };
							 | 
						|
								    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)
							 | 
						|
								    {
							 | 
						|
								        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)
							 | 
						|
								    {
							 | 
						|
								        let population = [];
							 | 
						|
								        for(let i = 0; i < populationSize; i++)
							 | 
						|
								        {
							 | 
						|
								            population.push(geneticChromosome.createRandomChromosome());
							 | 
						|
								        }
							 | 
						|
								        return population;
							 | 
						|
								    };
							 | 
						|
								    /**
							 | 
						|
								     * 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,
							 | 
						|
								                                            populationSize, maxGenerations,
							 | 
						|
								                                            desiredCost, mutationRate, keepNumber,
							 | 
						|
								                                            newBloodNumber)
							 | 
						|
								    {
							 | 
						|
								        let population = createRandomPopulation(geneticChromosome, populationSize);
							 | 
						|
								        let generation = 0;
							 | 
						|
								        let bestCost = Number.MAX_VALUE;
							 | 
						|
								        let bestChromosome = geneticChromosome;
							 | 
						|
								        do
							 | 
						|
								        {
							 | 
						|
								            matePopulation(population, populationSize);
							 | 
						|
								            newBlood(population, newBloodNumber);
							 | 
						|
								            mutatePopulation(population, mutationRate);
							 | 
						|
								            let generationResult = naturalSelection(population, keepNumber, costFunction);
							 | 
						|
								            if(bestCost > generationResult.bestFit)
							 | 
						|
								            {
							 | 
						|
								                bestChromosome = generationResult.bestChrom;
							 | 
						|
								                bestCost = generationResult.bestFit;
							 | 
						|
								            }
							 | 
						|
								            population = generationResult.survivors;
							 | 
						|
								            generation++;
							 | 
						|
								            console.log("Generation " + generation + " Best Cost: " + bestCost);
							 | 
						|
								        }while(generation < maxGenerations && bestCost > desiredCost);
							 | 
						|
								        return bestChromosome;
							 | 
						|
								    };
							 | 
						|
								    /**
							 | 
						|
								     * Ugly globals used to keep track of population state for the graph.
							 | 
						|
								     */
							 | 
						|
								    let genericChromosomeG, costFunctionG,
							 | 
						|
								        populationSizeG, maxGenerationsG,
							 | 
						|
								        desiredCostG, mutationRateG, keepNumberG,
							 | 
						|
								        newBloodNumberG, populationG, generationG,
							 | 
						|
								        bestCostG = Number.MAX_VALUE, bestChromosomeG = genericChromosomeG;
							 | 
						|
								    const runGeneticOptimizationForGraph = function()
							 | 
						|
								    {
							 | 
						|
								        let generationResult = naturalSelection(populationG, keepNumberG, costFunctionG);
							 | 
						|
								        stats.push([generationG, generationResult.bestFit, generationResult.average]);
							 | 
						|
								        if(bestCostG > generationResult.bestFit)
							 | 
						|
								        {
							 | 
						|
								            bestChromosomeG = generationResult.bestChrom;
							 | 
						|
								            bestCostG = generationResult.bestFit;
							 | 
						|
								        }
							 | 
						|
								        populationG = generationResult.survivors;
							 | 
						|
								        generationG++;
							 | 
						|
								        console.log("Generation " + generationG + " Best Cost: " + bestCostG);
							 | 
						|
								        console.log(generationResult);
							 | 
						|
								        matePopulation(populationG, populationSizeG);
							 | 
						|
								        newBlood(populationG, newBloodNumberG);
							 | 
						|
								        mutatePopulation(populationG, mutationRateG);
							 | 
						|
								        createGraph();
							 | 
						|
								    };
							 | 
						|
								    let stats = [];
							 | 
						|
								    const createGraph = function()
							 | 
						|
								    {
							 | 
						|
								        var dataPoints = [];
							 | 
						|
								        console.log(dataPoints);
							 | 
						|
								        var data = new google.visualization.DataTable();
							 | 
						|
								        data.addColumn('number', 'Gene 1');
							 | 
						|
								        data.addColumn('number', 'Gene 2');
							 | 
						|
								        for(let i = 0; i < populationG.length; i++)
							 | 
						|
								        {
							 | 
						|
								            data.addRow([populationG[i].getGenes()[0].getRealValue(),
							 | 
						|
								                populationG[i].getGenes()[1].getRealValue()]);
							 | 
						|
								        }
							 | 
						|
								        var options = {
							 | 
						|
								            title: 'Genetic Evolution On Two Genes Generation: ' + generationG,
							 | 
						|
								            hAxis: {title: 'Gene 1', minValue: 0, maxValue: 10},
							 | 
						|
								            vAxis: {title: 'Gene 2', minValue: 0, maxValue: 10},
							 | 
						|
								        };
							 | 
						|
								        var chart = new google.visualization.ScatterChart(document.getElementById('chart_div'));
							 | 
						|
								        chart.draw(data, options);
							 | 
						|
								        //line chart stuff
							 | 
						|
								        var line_data = new google.visualization.DataTable();
							 | 
						|
								        line_data.addColumn('number', 'Generation');
							 | 
						|
								        line_data.addColumn('number', 'Best');
							 | 
						|
								        line_data.addColumn('number', 'Average');
							 | 
						|
								        line_data.addRows(stats);
							 | 
						|
								        console.log(stats);
							 | 
						|
								        var lineChartOptions = {
							 | 
						|
								            hAxis: {
							 | 
						|
								                title: 'Generation'
							 | 
						|
								            },
							 | 
						|
								            vAxis: {
							 | 
						|
								                title: 'Cost'
							 | 
						|
								            },
							 | 
						|
								            colors: ['#AB0D06', '#007329']
							 | 
						|
								        };
							 | 
						|
								        var chart = new google.visualization.LineChart(document.getElementById('line_chart'));
							 | 
						|
								        chart.draw(line_data, lineChartOptions);
							 | 
						|
								    };
							 | 
						|
								    let gene1 = new Gene(1,10,10);
							 | 
						|
								    let gene2 = new Gene(1,10,0.4);
							 | 
						|
								    let geneList = [gene1, gene2];
							 | 
						|
								    let exampleOrganism = new Chromosome(geneList);
							 | 
						|
								    genericChromosomeG = exampleOrganism;
							 | 
						|
								    costFunctionG = basicCostFunction;
							 | 
						|
								    populationSizeG = 100;
							 | 
						|
								    maxGenerationsG = 30;
							 | 
						|
								    desiredCostG = 0.00001;
							 | 
						|
								    mutationRateG = 0.3;
							 | 
						|
								    keepNumberG = 30;
							 | 
						|
								    newBloodNumberG = 10;
							 | 
						|
								    generationG = 0;
							 | 
						|
								    function verifyForm()
							 | 
						|
								    {
							 | 
						|
								        if(Number($("#populationSize").val()) <= 1)
							 | 
						|
								        {
							 | 
						|
								            alert("Population size must be greater than one.");
							 | 
						|
								            return false;
							 | 
						|
								        }
							 | 
						|
								        if(Number($("#mutationRate").val()) > 1 ||
							 | 
						|
								            Number($("#mutationRate").val()) < 0)
							 | 
						|
								        {
							 | 
						|
								            alert("Mutation rate must be between zero and one.");
							 | 
						|
								            return false;
							 | 
						|
								        }
							 | 
						|
								        if(Number($("#survivalSize").val()) < 0)
							 | 
						|
								        {
							 | 
						|
								            alert("Survival size can't be less than one.");
							 | 
						|
								            return false;
							 | 
						|
								        }
							 | 
						|
								        if(Number($("#newBlood").val()) < 0)
							 | 
						|
								        {
							 | 
						|
								            alert("New organisms can't be a negative number.");
							 | 
						|
								            return false;
							 | 
						|
								        }
							 | 
						|
								        return true;
							 | 
						|
								    }
							 | 
						|
								    function resetPopulation()
							 | 
						|
								    {
							 | 
						|
								        if(verifyForm())
							 | 
						|
								        {
							 | 
						|
								            stats = [];
							 | 
						|
								            autoRunning = false;
							 | 
						|
								            $("#runAutoOptimizer").val("Auto Run");
							 | 
						|
								            populationSizeG = $("#populationSize").val();
							 | 
						|
								            mutationRateG = $("#mutationRate").val();
							 | 
						|
								            keepNumberG = $("#survivalSize").val();
							 | 
						|
								            newBloodNumberG = $("#newBlood").val();
							 | 
						|
								            generationG = 0;
							 | 
						|
								            populationG = createRandomPopulation(genericChromosomeG, populationSizeG);
							 | 
						|
								            createGraph();
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								    populationG = createRandomPopulation(genericChromosomeG, populationSizeG);
							 | 
						|
								    window.onload = function (){
							 | 
						|
								        google.charts.load('current', {packages: ['corechart', 'line']});
							 | 
						|
								        google.charts.load('current', {'packages':['corechart']}).then(function()
							 | 
						|
								        {
							 | 
						|
								            createGraph();
							 | 
						|
								        })
							 | 
						|
								    };
							 | 
						|
								    let autoRunning = false;
							 | 
						|
								    function runAutoOptimizer()
							 | 
						|
								    {
							 | 
						|
								        if(autoRunning === true)
							 | 
						|
								        {
							 | 
						|
								            runGeneticOptimizationForGraph();
							 | 
						|
								            setTimeout(runAutoOptimizer, 1000);
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								    function startStopAutoRun()
							 | 
						|
								    {
							 | 
						|
								        autoRunning = !autoRunning;
							 | 
						|
								        if(autoRunning)
							 | 
						|
								        {
							 | 
						|
								            $("#runAutoOptimizer").val("Stop Auto Run");
							 | 
						|
								        }
							 | 
						|
								        else
							 | 
						|
								        {
							 | 
						|
								            $("#runAutoOptimizer").val("Resume Auto Run");
							 | 
						|
								        }
							 | 
						|
								        runAutoOptimizer();
							 | 
						|
								    }
							 | 
						|
								</script>
							 | 
						|
								<div id="chart_div"></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="runAutoOptimizer" onclick='startStopAutoRun()' type="button" value="Auto Run">
							 | 
						|
								<br>
							 | 
						|
								<br>
							 | 
						|
								<div class="card">
							 | 
						|
								    <div class="card-header">
							 | 
						|
								        <h2>Population Variables</h2>
							 | 
						|
								    </div>
							 | 
						|
								    <form class="card-body">
							 | 
						|
								        <div class="row p-2">
							 | 
						|
								            <div class="col">
							 | 
						|
								                <label for="populationSize">Population Size</label>
							 | 
						|
								                <input type="text" class="form-control" value="100" id="populationSize" placeholder="Population Size" required>
							 | 
						|
								            </div>
							 | 
						|
								            <div class="col">
							 | 
						|
								                <label for="populationSize">Survival Size</label>
							 | 
						|
								                <input type="text" class="form-control" value="20" id="survivalSize" placeholder="Survival Size" required>
							 | 
						|
								            </div>
							 | 
						|
								        </div>
							 | 
						|
								        <div class="row p-2">
							 | 
						|
								            <div class="col">
							 | 
						|
								                <label for="populationSize">Mutation Rate</label>
							 | 
						|
								                <input type="text" class="form-control" value="0.03" id="mutationRate" placeholder="Mutation Rate" required>
							 | 
						|
								            </div>
							 | 
						|
								            <div class="col">
							 | 
						|
								                <label for="populationSize">New Organisms Per Generation</label>
							 | 
						|
								                <input type="text" class="form-control" value="5" id="newBlood" placeholder="New Organisms" required>
							 | 
						|
								            </div>
							 | 
						|
								        </div>
							 | 
						|
								        <br>
							 | 
						|
								        <input class='btn btn-primary' id="reset" onclick='resetPopulation()' type="button" value="Reset Population">
							 | 
						|
								    </form>
							 | 
						|
								</div>
							 |