My code denotes an open source library that makes creating genetic algorithms easier. It's an encapsulated library and is called using the new
constructor.
Initialize the genetic algorithm by writing:
var my_algo = new olog.GeneticAlgorithm("target", config)
and evolve it by writing:
my_algo.progress([however many times I want it to run, leave blank if you want it to run till found fittest])
You can find the CodeShare here. Any review is great. Also, I chose object over class due to preference.
(function(exports) {
"use strict";
exports.GeneticAlgorithm = function(input, configurations = {mutation_rate: 0.005, maximum_population: 1500, expansion_multiplier: 10}) {
/*
METHODS:
generate_population => population Array (has no fitness scores)
assess_population => populaton Array (has fitness scores)
generate_mating_pool => mating_pool Array (not connected to parent object)
crossover => population Array (adaptation of generate_population)
mutate => population Array (mutates the existing population)
evaluate => results Object (contains a resultation of the algorithm)
*/
var genetic_algorithm = {
//Setting up data and variables
input: input,
configurations: {
mutation_rate: (configurations.mutation_rate),
maximum_population: (configurations.maximum_population),
expansion_multiplier: (configurations.expansion_multiplier)
},
algorithm_data: {
generations: 0,
input_type: undefined,
average_fitness_score: 0,
time_began: Date.now()
},
population: {
current_population: [],
mating_pool: []
},
//Defining library methods
//Generates the population
generate_population: (function(input, input_type, maximum_population, string_generator, number_generator, expansion_multiplier) {
/*
input => The input, or target of the algorithm (...)
input_type => The type of the input, as defined by another method ("string" | "number")
maximum_population => The maximum number of individuals that the population can hold (0,Inf)
string_generator => The function that generates a random string (func)
number_generator => The function that generates a random number (func)
expansion_multiplier => A number that determines how far the algorithm should look (0, Inf)
*/
//The population that this method returns
var return_population = [];
if (input_type === "string") {
for (let i = 0; i < maximum_population; i++) {
//Add an individual (string) to the population, baby boom that thing!
return_population.push({
individual: string_generator(input.length, number_generator),
fitness_score: undefined
});
}
return return_population;
} else if (input_type === "number") {
for (let i = 0; i < maximum_population; i++) {
//Add an individual (number) to that population, it's a boy!
return_population.push({
individual: number_generator((input * -1 * expansion_multiplier), (input * expansion_multiplier)),
fitness_score: undefined
});
}
return return_population;
}
}),
//Assesses the population
assess_population: (function(input, population, input_type) {
/*
input => The input to the algorithm, showing a or the prime example (...)
population => The collection of individuals (...)
input_type => The type of the or a prime example ("string" | "number")
*/
//The population that this method returns
var return_population = [];
var fitness_score;
if (input_type === "string") {
//Checks the population individual by the input character-by-character
for (let i = 0; i < population.length; i++) {
fitness_score = 0;
if (population[i].fitness_score === undefined) {
for (let j = 0; j < population[i].individual.length; j++) {
if (population[i].individual[j] === input[j]) {
fitness_score++;
}
}
return_population.push({
individual: population[i].individual,
fitness_score: fitness_score
});
} else {
return_population.push(population[i]);
}
}
return return_population;
} else if (input_type === "number") {
//Finds by how much the individual deviates from the input
for (let i = 0; i < population.length; i++) {
fitness_score = Math.abs(population[i].individual - input);
return_population.push({
individual: population[i].individual,
fitness_score: fitness_score
});
return return_population;
}
}
}),
//Generates the mating pool
generate_mating_pool: (function(population) {
/*
population => The collection of individuals in the population (...)
*/
var maximum_fitness = 0;
//The mating_pool that this method returns
var return_mating_pool = [];
for (let i = 0; i < population.length; i++) {
//Gets the maximum fitness scored individual
if (population[i].fitness_score > maximum_fitness) {
maximum_fitness = population[i].fitness_score;
}
}
for (let i = 0; i < population.length; i++) {
for (let j = 0; j < ((Math.floor(population[i].fitness_score / maximum_fitness) * 100) || 1); j++) {
return_mating_pool.push(population[i]);
}
}
return return_mating_pool;
}),
//Cross the individuals over
crossover: (function(mating_pool, number_generator) {
/*
mating_pool => A pool of biased individuals (...)
number_generator => A function that generates a random number (func)
*/
//The population that this method returns
var return_population = [];
var dividing_point;
var male;
var male_genes;
var female;
var female_genes;
var child;
var child_genes;
for (let i = 0; i < mating_pool.length; i++) {
male = true;
female = true;
child = "";
child_genes = [];
dividing_point = 0;
//Keeps repeating until the male individual does not equal the female individual
while (male === female) {
male = mating_pool[Math.floor(number_generator(0, mating_pool.length))];
female = mating_pool[Math.floor(number_generator(0, mating_pool.length))];
male_genes = male.individual.split("");
female_genes = female.individual.split("");
}
dividing_point = Math.floor(number_generator(0, male_genes.length));
//Splits genes up for child crossover
for (let j = 0; j < male_genes.length; j++) {
if (j < dividing_point) {
child_genes.push(male_genes[j]);
} else {
child_genes.push(female_genes[j]);
}
}
child = {
individual: child_genes.join(""),
fitness_score: undefined
};
return_population.push(child);
}
return return_population;
}),
//Mutates the population
mutate: (function(input, input_type, population, mutation_rate, number_generator, string_generator, expansion_multiplier) {
/*
input => The or a perfect example the algorithm should pursue (...)
input_type => The type of the or a perfect example the algorithm should pursue (string | number)
population => Collection of individuals (...)
mutation_rate => The rate at which the population mutates [0,1]
number_generator => A function that generates a random number (func)
string_generator => A function that generates a random string (func)
expansion_multiplier => A value that tells the algorithm how far it should look for a number (0, Inf)
*/
//The population that this method returns
var return_population = [];
var mutation_determiner = number_generator(0, 1);
for (let i = 0; i < population.length; i++) {
if (mutation_determiner < mutation_rate) {
if (input_type === "string") {
return_population.push({
individual: string_generator(population[i].individual.length, number_generator),
fitness_score: undefined
});
} else if (input_type === "number") {
return_population.push({
individual: number_generator((input * -1 * expansion_multiplier), (input * expansion_multiplier)),
fitness_score: undefined
});
}
} else {
return_population.push({
individual: population[i].individual,
fitness_score: undefined
});
}
}
return return_population;
}),
//Evaluates the current fittest
evaluate_population: (function(input, population, generations, mutation_rate, expansion_multiplier, maximum_population, average_fitness_score, time_began) {
/*
input => The or a prime example that the algorithm should model (...)
population => The collection of individuals (...)
generations => Measures how many generations the algorithm has been through (0,Inf)
mutation_rate => Is here to tell the user what they set the mutation rate to [0,1]
expansion_multiplier => Here to tell what the user set the expansion multiplier to (0, Inf)
maximum_population => Tells the user what they set the maximum population to (0, Inf)
average_fitness_score => Tells the method the average fitness score of the algorithm (0, Inf)
time_began => The point in time where the genetic algorithm was started
*/
//The results the program returns
var return_results = {};
var has_found_fittest = false;
var fittest = {};
var average_accuracy = 0;
var time_elapsed = 0;
for (let i = 0; i < population.length; i++) {
if (population[i].individual === input) {
has_found_fittest = true;
fittest = population[i];
break;
}
}
average_accuracy = (average_fitness_score / input.length);
time_elapsed = (Date.now() - time_began);
//Telling the receiver that the algorithm found the fittest
return_results.has_found_fittest = has_found_fittest;
return_results.fittest_individual = fittest;
return_results.population = population;
return_results.generations = generations;
return_results.mutation_rate = mutation_rate;
return_results.expansion_multiplier = expansion_multiplier;
return_results.maximum_population = maximum_population;
return_results.average_fitness_score = average_fitness_score;
return_results.average_accuracy = average_accuracy;
return_results.time_elapsed = time_elapsed;
return return_results;
}),
//Generates a random number
generate_random_number: (function(minimum, maximum) {
/*
minimum => The minimum number the function should generate (-Inf, Inf)
maximum => The maximum number the function should generate (-Inf, Inf)
*/
//The return number the method returns
var return_number = 0;
return_number = Math.random() * (maximum - minimum) + minimum;
return return_number;
}),
//Generates a random string
generate_random_string: (function(length, number_generator) {
/*
length => Tells the method how long the return string should be (0,Inf)
*/
//The string that this method will be returning
var return_string = "";
for (let i = 0; i < length; i++) {
return_string += String.fromCharCode(number_generator(0, 65535));
}
return return_string;
}),
//Identifies the data type
get_algorithm_data_type: (function(input) {
/*
input => Tells the method the input so it can find out the data type (...)
*/
if (typeof input === "undefined") return;
if (typeof input === "string") return "string";
if (typeof input === "number") return "number";
if (typeof input === "boolean") {
input = input.toString();
return "string";
}
if (Array.isArray(input)) {
input = input.join(" ");
return "string";
}
if (typeof input === "object") {
input = JSON.stringify(input);
return "string";
}
}),
//Gets the average fitness score
get_average_fitness_score: (function(population) {
/*
population => Feeds the method the fitness scores (...)
*/
//The average number the method returns
var return_average = 0;
var population_sum = 0;
for (let i = 0; i < population.length; i++) {
population_sum += (population[i].fitness_score || 0);
}
return_average = population_sum / population.length;
return return_average;
}),
//Progresses the algorithm
progress: (function(iterations) {
/*
iterations => The amount of iterations that the algorithm should progress through (0, Inf)
*/
var return_results = {};
var found_fittest = false;
for (let i = 0; i < (iterations || Infinity); i++) {
//Asses has a lot of parameters: input, population, input_type
this.population.current_population = this.assess_population(this.input, this.population.current_population, this.algorithm_data.input_type);
this.algorithm_data.average_fitness_score = this.get_average_fitness_score(this.population.current_population);
this.population.mating_pool = this.generate_mating_pool(this.population.current_population);
this.population.current_population = this.crossover(this.population.mating_pool, this.generate_random_number);
//Mutate has a lot of parameters: input, input_type, population, mutation_rate, number_generator, string_generator, expansion_multiplier
this.population.current_population = this.mutate(this.input, this.algorithm_data_type, this.population.current_population, this.configurations.mutation_rate, this.generate_random_number, this.generate_random_string, this.configurations.expansion_multiplier);
//Add a generation to the count
this.algorithm_data.generations++;
//Evaluation takes in a lot of arguments: input, population, generations, mutation_rate, expansion_multiplier, maximum_population, average_fitness_score, time_began
return_results = this.evaluate_population(this.input, this.population.current_population, this.algorithm_data.generations, this.configurations.mutation_rate, this.configurations.expansion_multiplier, this.configurations.maximum_population, this.algorithm_data.average_fitness_score, this.algorithm_data.time_began);
found_fittest = return_results.has_found_fittest;
if (found_fittest === true) {
break;
}
}
return return_results;
})
};
if (input) {
genetic_algorithm.algorithm_data.input_type = genetic_algorithm.get_algorithm_data_type(genetic_algorithm.input);
//Due to generate's large number of parameters, these are all of them: input, input_type, maximum_population, string_generator, number_generator, expansion_multiplier
genetic_algorithm.population.current_population = genetic_algorithm.generate_population(genetic_algorithm.input, genetic_algorithm.algorithm_data.input_type, genetic_algorithm.configurations.maximum_population, genetic_algorithm.generate_random_string, genetic_algorithm.generate_random_number, genetic_algorithm.configurations.expansion_multiplier);
return genetic_algorithm;
} else {
throw TypeError("NO_INPUT_FOUND: No input found. Try giving the GeneticAlgorithm() function a argument; the argument represents what you want the algorithm to pursue.");
}
}
})(typeof exports === 'undefined'? this['olog']={}: exports)