-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Refactor import paths in DeDuplicator.ts, CreatureUUID.ts, Breed.ts, ExperimentStore.ts, and Creature.ts * Refactor sortCreaturesByScore function and add verbose logging in ElitismUtils.ts * Refactor checkAndAdd function in Neat.ts to improve readability and fix error handling * Refactor checkAndAdd function in Neat.ts to improve readability and error handling * Refactor Genus class to add findClosestMatchingSpecies method * Refactor Genus class to add findClosestMatchingSpecies method * Fix error handling in Creature.ts and CreatureState.ts * Fix bias range check in Propagate/STEP.ts and add missing synapses in FineTune.ts * Fix formatting in Creature.ts and FineTune.ts * Refactor import paths in DeDuplicator.ts, CreatureUUID.ts, Breed.ts, ExperimentStore.ts, and Creature.ts * Refactor error handling and import paths in Genus.ts, Neat.ts, CreatureUUID.ts, Breed.ts, ExperimentStore.ts, and Creature.ts --------- Co-authored-by: Nigel Leck <[email protected]>
- Loading branch information
Showing
11 changed files
with
333 additions
and
256 deletions.
There are no files selected for viewing
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
import { Creature, Selection } from "../../mod.ts"; | ||
import { Offspring } from "../architecture/Offspring.ts"; | ||
import { NeatConfig } from "../config/NeatConfig.ts"; | ||
import { Genus } from "./Genus.ts"; | ||
|
||
export class Breed { | ||
private genus: Genus; | ||
readonly config: NeatConfig; | ||
|
||
constructor(genus: Genus, config: NeatConfig) { | ||
this.genus = genus; | ||
this.config = config; | ||
} | ||
/** | ||
* Breeds two parents into an offspring, population MUST be sorted | ||
*/ | ||
breed(): Creature | undefined { | ||
const mum = this.getParent(this.genus.population); | ||
|
||
if (mum === undefined) { | ||
console.warn( | ||
"No mother found", | ||
this.config.selection.name, | ||
this.genus.population.length, | ||
); | ||
|
||
return; | ||
} | ||
|
||
const dad = this.getDad(mum); | ||
if (dad === undefined) { | ||
console.warn( | ||
"No father found", | ||
); | ||
|
||
return; | ||
} | ||
|
||
const creature = Offspring.breed( | ||
mum, | ||
dad, | ||
); | ||
|
||
return creature; | ||
} | ||
|
||
getDad(mum: Creature): Creature { | ||
if (mum.uuid === undefined) throw new Error(`mum.uuid is undefined`); | ||
|
||
const species = this.genus.findSpeciesByCreatureUUID(mum.uuid); | ||
|
||
let possibleFathers = species.creatures.filter((creature) => | ||
creature.uuid !== mum.uuid | ||
); | ||
|
||
if (possibleFathers.length === 0) { | ||
const closestSpecies = this.genus.findClosestMatchingSpecies(mum); | ||
if (closestSpecies) { | ||
possibleFathers = closestSpecies.creatures; | ||
} | ||
} | ||
|
||
return this.getParent(possibleFathers); | ||
} | ||
|
||
/** | ||
* Gets a parent based on the selection function | ||
* @return {Creature} parent | ||
*/ | ||
getParent(population: Creature[]): Creature { | ||
switch (this.config.selection) { | ||
case Selection.POWER: { | ||
const r = Math.random(); | ||
const index = Math.floor( | ||
Math.pow(r, Selection.POWER.power) * | ||
population.length, | ||
); | ||
|
||
return population[index]; | ||
} | ||
case Selection.FITNESS_PROPORTIONATE: { | ||
/** | ||
* As negative fitnesses are possible | ||
* https://stackoverflow.com/questions/16186686/genetic-algorithm-handling-negative-fitness-values | ||
* this is unnecessarily run for every individual, should be changed | ||
*/ | ||
|
||
let totalFitness = 0; | ||
let minimalFitness = 0; | ||
for (let i = population.length; i--;) { | ||
const tmpScore = population[i].score; | ||
const score = tmpScore === undefined ? Infinity * -1 : tmpScore; | ||
minimalFitness = score < minimalFitness ? score : minimalFitness; | ||
totalFitness += score; | ||
} | ||
|
||
const adjustFitness = Math.abs(minimalFitness); | ||
totalFitness += adjustFitness * population.length; | ||
|
||
const random = Math.random() * totalFitness; | ||
let value = 0; | ||
|
||
for (let i = 0; i < population.length; i++) { | ||
const genome = population[i]; | ||
if (genome.score !== undefined) { | ||
value += genome.score + adjustFitness; | ||
if (random < value) { | ||
return genome; | ||
} | ||
} | ||
} | ||
|
||
// if all scores equal, return random genome | ||
return population[ | ||
Math.floor(Math.random() * population.length) | ||
]; | ||
} | ||
case Selection.TOURNAMENT: { | ||
if (Selection.TOURNAMENT.size > this.config.populationSize) { | ||
throw new Error( | ||
"Your tournament size should be lower than the population size, please change Selection.TOURNAMENT.size", | ||
); | ||
} | ||
|
||
// Create a tournament | ||
const individuals = new Array(Selection.TOURNAMENT.size); | ||
for (let i = 0; i < Selection.TOURNAMENT.size; i++) { | ||
const random = population[ | ||
Math.floor(Math.random() * population.length) | ||
]; | ||
individuals[i] = random; | ||
} | ||
|
||
// Sort the tournament individuals by score | ||
individuals.sort(function (a, b) { | ||
return b.score - a.score; | ||
}); | ||
|
||
// Select an individual | ||
for (let i = 0; i < Selection.TOURNAMENT.size; i++) { | ||
if ( | ||
Math.random() < Selection.TOURNAMENT.probability || | ||
i === Selection.TOURNAMENT.size - 1 | ||
) { | ||
return individuals[i]; | ||
} | ||
} | ||
throw new Error(`No parent found in tournament`); | ||
} | ||
default: { | ||
throw new Error(`Unknown selection: ${this.config.selection}`); | ||
} | ||
} | ||
} | ||
} |
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import { removeTag } from "https://deno.land/x/[email protected]/mod.ts"; | ||
import { creatureValidate } from "../architecture/CreatureValidate.ts"; | ||
import { Creature, Mutation } from "../../mod.ts"; | ||
import { CreatureInternal } from "../architecture/CreatureInterfaces.ts"; | ||
import { NeatConfig } from "../config/NeatConfig.ts"; | ||
|
||
export class Mutator { | ||
private config: NeatConfig; | ||
constructor(config: NeatConfig) { | ||
this.config = config; | ||
} | ||
|
||
/** | ||
* Mutates the given (or current) population | ||
*/ | ||
mutate(creatures: Creature[]): void { | ||
for (let i = creatures.length; i--;) { | ||
if (Math.random() <= this.config.mutationRate) { | ||
const creature = creatures[i]; | ||
if (this.config.debug) { | ||
creatureValidate(creature); | ||
} | ||
for (let j = this.config.mutationAmount; j--;) { | ||
const mutationMethod = this.selectMutationMethod(creature); | ||
|
||
creature.mutate( | ||
mutationMethod, | ||
Math.random() < this.config.focusRate | ||
? this.config.focusList | ||
: undefined, | ||
); | ||
} | ||
|
||
if (this.config.debug) { | ||
creatureValidate(creature); | ||
} | ||
|
||
removeTag(creature, "approach"); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Selects a random mutation method for a genome according to the parameters | ||
*/ | ||
private selectMutationMethod(creature: CreatureInternal) { | ||
const mutationMethods = this.config | ||
.mutation; | ||
|
||
for (let attempts = 0; true; attempts++) { | ||
const mutationMethod = mutationMethods[ | ||
Math.floor(Math.random() * this.config.mutation.length) | ||
]; | ||
|
||
if ( | ||
mutationMethod === Mutation.ADD_NODE && | ||
creature.neurons.length >= this.config.maximumNumberOfNodes | ||
) { | ||
continue; | ||
} | ||
|
||
if ( | ||
mutationMethod === Mutation.ADD_CONN && | ||
creature.synapses.length >= this.config.maxConns | ||
) { | ||
continue; | ||
} | ||
|
||
return mutationMethod; | ||
} | ||
} | ||
} |
Oops, something went wrong.