Skip to content

Commit

Permalink
Add WIP speciate function to Population, Population.evolve
Browse files Browse the repository at this point in the history
  • Loading branch information
christianechevarria committed Jan 13, 2020
1 parent fc01396 commit caa73b6
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 19 deletions.
66 changes: 52 additions & 14 deletions src/architecture/population.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,19 @@ const Population = function(options) {
/**
* Creates an array of networks, each network with a different set of weights, then returns it.
*
* @function getPopulation
*
* Note: neat spec technically dictates that each network be exactly equivalent.
*
* @alpha
*
* @private
*
*
* @function getPopulation
* @memberof Population
*
* @return {Network[]} Returns an array of networks. Must be assigned to Population.members if replacing current population.
*
* @todo Add template network functionality
* @todo Add option to have identical population networks
*/
self.getPopulation = function create_networks(options={}) {
const population = []
Expand Down Expand Up @@ -122,7 +126,7 @@ const Population = function(options) {
if(large) return 1;
if(small) return -1;
else return number;
}
}

// 90% chance slight change, +-0.5 at most | 10% chance new random value, between -1 and 1
conn.weight = (random < .9) ? clamp(conn.weight + number() / 2) : number();
Expand Down Expand Up @@ -151,17 +155,13 @@ const Population = function(options) {
* @return {network[]} An evolved population
*
* @todo Reintroduce mutation flexibility
* @todo Add speciation code
* @todo Add compatabilityDistance method, part of speciation section
* @todo Add stagnation-driven population removal
* @todo Add createOffspring code
*/
self.evolve = async function(options={}) {

/**
* ToDo Section
* // Break population members into their respective species
* self.species = self.speciate(self.species);
*/
// Break population members into their respective species
self.species = self.speciate(self.members, self.species);

// Mutates ".members" directly. Expected behavior
self.members = self.members.map(network => self.mutate(network))
Expand Down Expand Up @@ -192,12 +192,51 @@ const Population = function(options) {
*
* @alpha
*
* @expects speciesArray to be stored by fittest Species to least fit
*
* @function speciate
* @memberof Population
*
* @return {void}
* @param {Network[]} members An array of population members to be placed in species
* @param {Species[]} speciesArray The previous set of population species
*
* @return {Species[]} The new set of population species with corresponding members of the population inside
*
* @todo Implement Duff's device to reduce iterations
* @todo Figure out a way to get rid of nested foor loop
* @todo Consider making this an async function
* @todo Benchmark
*/
self.speciate = function (speciesArray) {}
self.speciate = function (members, speciesArray) {

// First clear the members of each species
speciesArray = speciesArray.map(species => {
species.members = [];
return species;
})

// "outer" is a loop label. Avoids checking whether genome was placed in species inside inner-loop.
outer:
for (let i = 0; i < members.length; i++) {
const genome = members[i];

for(let j = 0; i < speciesArray.length; j++) {
const species = speciesArray[i];

// Determine if genome belongs in species
if(species.isCompatible([species.representative, genome])) {
// Add genome to species
species.addMember(genome); continue outer; // <- Use "outer" to skip to next genome.
}

}

// Only runs if genome was not placed in species
speciesArray.push(new Species(genome));
}

return speciesArray;
}

/**
* Selects two genomes from the population with `getParent()`, and returns the offspring from those parents.
Expand Down Expand Up @@ -266,7 +305,6 @@ const Population = function(options) {
return members[0];
};


/**
* Returns the average fitness of the current population.
*
Expand Down
10 changes: 5 additions & 5 deletions src/architecture/species.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,15 @@ const Species = function(genome, options) {
// NEAT designated props
self.members = [];
self.members.push(genome.clone()); // Genome required at creation
self.representative = genome.clone(); // Genome is representative by default
self.representative = genome.clone(); // Genome is "representative" by default
self.fittest = genome.clone(); // Genome is fittest. AKA "champion" in NEAT literature.
self.stagnation = 0; // Generation count without improvement

// Adjustable props - compatibility
self.coefficient = options.coefficient || [1,1,0.4];
self.threshold = 3;
self.threshold = 3; // Threshold for determining whether or not a genome is compatible with the species

// Species metadata
self.fittest = genome.clone();
self.bestFitness = 0;
self.averageFitness = 0;

Expand All @@ -59,14 +59,14 @@ const Species = function(genome, options) {
*
* @alpha
*
* @expects Genomes to have connections in ascending order by their .id properties (as dictated by NEAT)
* @expects Genomes connection ids to be in ascending order i.e. genome.connections = [{ ..., id: 1 }, { ..., id: 2 }, { ..., id: 3 }]
*
* @function getDistance
* @memberof Species
*
* @param {Network[]} genomes Array containing two genomes
* @param {object} options
* @param {number[]} options.weights An array of weights for excess, disjoint, and matching gene connection weight distance sum
* @param {number[]} options.weights An array of weights for excess, disjoint, and matching gene connection-weight distance sum
* @param {number} options.threshold The minimum node size required to use a normalization factor in genomic distance
*
* @return {number} A genomic disntance between genomes
Expand Down

0 comments on commit caa73b6

Please sign in to comment.