From 4d2e5b4b442edf66e198f8d4bda497a76a5d8ad3 Mon Sep 17 00:00:00 2001 From: Michael Peters Date: Wed, 28 Aug 2024 23:27:58 -0700 Subject: [PATCH] adjust chooseSurvivors -> chooseByFertility to also give the champions --- src/site/snake/brain-neat.ts | 38 +++++++++++++++++++++++++++--------- src/test/test-brain-neat.ts | 12 ++++++++---- 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/src/site/snake/brain-neat.ts b/src/site/snake/brain-neat.ts index 66bee99..77bfe94 100644 --- a/src/site/snake/brain-neat.ts +++ b/src/site/snake/brain-neat.ts @@ -348,12 +348,12 @@ export function initialSpeciate( return population; } -interface SurvivorsConfig { - survival_threshold: number; +interface FertilityConfig { + fertile_threshold: number; champion_min_species_size: number; } -export function chooseSurvivors(population: Population, fitness: Map, config: SurvivorsConfig) { - const { survival_threshold, champion_min_species_size } = config; +export function chooseByFertility(population: Population, fitness: Map, config: FertilityConfig) { + const { fertile_threshold, champion_min_species_size } = config; const species = mapInvert(population); @@ -364,22 +364,22 @@ export function chooseSurvivors(population: Population, fitness: Map(); + const fertile = new Set(); // add sufficiently fit organisms const genomes: Genome[] = Array.from(population.keys()); - const survival_idx = Math.floor(genomes.length * survival_threshold); + const survival_idx = Math.floor(genomes.length * fertile_threshold); genomes.sort((a, b) => adjFitness.get(a)! - adjFitness.get(b)!); - genomes.slice(survival_idx).forEach(o => survivors.add(o)); + genomes.slice(survival_idx).forEach(o => fertile.add(o)); // add species champions const champions = mapMap(species, (k, v) => [k, keyMax(v, o => adjFitness.get(o)!)]); for (const [sid, c] of champions.entries()) { if (species.get(sid)!.size < champion_min_species_size) continue; - survivors.add(c); + fertile.add(c); } - return survivors; + return { fertile, champions }; } interface MateChoiceConfig { @@ -594,6 +594,26 @@ export function mutate(genome: Genome, config: MutateConfig): Genome { return newGenome; } +interface NextGenerationConfig { + fc: FertilityConfig; +} +export function computeNextGeneration( + population: Population, + fitness: Map, + config: NextGenerationConfig, +) { + const { fc } = config; + + const { fertile, champions } = chooseByFertility(population, fitness, fc); + + // TODO: only copy over champions, the rest mate to produce the population + const nextGeneration = new Map(Array.from(fertile).map(s => [s, population.get(s)!])); + const species = mapInvert(nextGeneration); + const reps = mapMap(species, (sid, genomes) => [sid, genomes.values().next().value]); + + // TODO: update chooseSurvivors so champions get copied over +} + export function activate(n: number) { // modified sigmoid function from NEAT paper return 1 / (1 + Math.exp(-4.9 * n)); diff --git a/src/test/test-brain-neat.ts b/src/test/test-brain-neat.ts index 9994b8f..c180851 100644 --- a/src/test/test-brain-neat.ts +++ b/src/test/test-brain-neat.ts @@ -2,7 +2,7 @@ import { activate, alignGenomes, chooseMate, - chooseSurvivors, + chooseByFertility, compatibilityDistance, crossGenomes, findAcyclicNewConns, @@ -192,11 +192,11 @@ function testChooseSurvivors() { // (1 - 4/6) = 2/6 survive by fitness alone // top genome (champion) from all species with at least 3 genomes should always survive - const sc = { survival_threshold: 4 / 6, champion_min_species_size: 3 }; - const survivors = chooseSurvivors(population, fitness, sc); + const sc = { fertile_threshold: 4 / 6, champion_min_species_size: 3 }; + const { fertile, champions } = chooseByFertility(population, fitness, sc); assertSetEqual( - survivors, + fertile, new Set([ genomeB, // champion of species 1 genomeD, // #2 fitness @@ -204,6 +204,10 @@ function testChooseSurvivors() { // genomeF not included since species is not large enough to have a champion ]), ); + assert(champions.size === 3); + assert(champions.get(1)! === genomeB); + assert(champions.get(2)! === genomeE); + assert(champions.get(3)! === genomeF); } addTest(testChooseSurvivors);