adjust chooseSurvivors -> chooseByFertility to also give the champions

This commit is contained in:
Michael Peters 2024-08-28 23:27:58 -07:00
parent 21bf8f781e
commit 4d2e5b4b44
2 changed files with 37 additions and 13 deletions

View File

@ -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<Genome, number>, config: SurvivorsConfig) {
const { survival_threshold, champion_min_species_size } = config;
export function chooseByFertility(population: Population, fitness: Map<Genome, number>, 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<Genome, num
return [k, v / speciesSize];
});
const survivors = new Set<Genome>();
const fertile = new Set<Genome>();
// 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<Genome, number>,
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));

View File

@ -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);