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; return population;
} }
interface SurvivorsConfig { interface FertilityConfig {
survival_threshold: number; fertile_threshold: number;
champion_min_species_size: number; champion_min_species_size: number;
} }
export function chooseSurvivors(population: Population, fitness: Map<Genome, number>, config: SurvivorsConfig) { export function chooseByFertility(population: Population, fitness: Map<Genome, number>, config: FertilityConfig) {
const { survival_threshold, champion_min_species_size } = config; const { fertile_threshold, champion_min_species_size } = config;
const species = mapInvert(population); const species = mapInvert(population);
@ -364,22 +364,22 @@ export function chooseSurvivors(population: Population, fitness: Map<Genome, num
return [k, v / speciesSize]; return [k, v / speciesSize];
}); });
const survivors = new Set<Genome>(); const fertile = new Set<Genome>();
// add sufficiently fit organisms // add sufficiently fit organisms
const genomes: Genome[] = Array.from(population.keys()); 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.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 // add species champions
const champions = mapMap(species, (k, v) => [k, keyMax(v, o => adjFitness.get(o)!)]); const champions = mapMap(species, (k, v) => [k, keyMax(v, o => adjFitness.get(o)!)]);
for (const [sid, c] of champions.entries()) { for (const [sid, c] of champions.entries()) {
if (species.get(sid)!.size < champion_min_species_size) continue; if (species.get(sid)!.size < champion_min_species_size) continue;
survivors.add(c); fertile.add(c);
} }
return survivors; return { fertile, champions };
} }
interface MateChoiceConfig { interface MateChoiceConfig {
@ -594,6 +594,26 @@ export function mutate(genome: Genome, config: MutateConfig): Genome {
return newGenome; 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) { export function activate(n: number) {
// modified sigmoid function from NEAT paper // modified sigmoid function from NEAT paper
return 1 / (1 + Math.exp(-4.9 * n)); return 1 / (1 + Math.exp(-4.9 * n));

View File

@ -2,7 +2,7 @@ import {
activate, activate,
alignGenomes, alignGenomes,
chooseMate, chooseMate,
chooseSurvivors, chooseByFertility,
compatibilityDistance, compatibilityDistance,
crossGenomes, crossGenomes,
findAcyclicNewConns, findAcyclicNewConns,
@ -192,11 +192,11 @@ function testChooseSurvivors() {
// (1 - 4/6) = 2/6 survive by fitness alone // (1 - 4/6) = 2/6 survive by fitness alone
// top genome (champion) from all species with at least 3 genomes should always survive // 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 sc = { fertile_threshold: 4 / 6, champion_min_species_size: 3 };
const survivors = chooseSurvivors(population, fitness, sc); const { fertile, champions } = chooseByFertility(population, fitness, sc);
assertSetEqual( assertSetEqual(
survivors, fertile,
new Set([ new Set([
genomeB, // champion of species 1 genomeB, // champion of species 1
genomeD, // #2 fitness genomeD, // #2 fitness
@ -204,6 +204,10 @@ function testChooseSurvivors() {
// genomeF not included since species is not large enough to have a champion // 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); addTest(testChooseSurvivors);