neuroevolution_speciation (faber_neuroevolution v1.2.4)
View SourceNEAT-style speciation for neuroevolution.
This module implements speciation - grouping individuals into species based on genetic similarity. Species enable niching, allowing diverse strategies to coexist and explore different fitness peaks without competing directly.
Compatibility Distance
For fixed-topology networks (same structure, different weights), compatibility is measured as the average absolute weight difference: distance(A, B) = (1/N) * sum(|w_a_i - w_b_i|)
Individuals with distance below the compatibility threshold belong to the same species.
Species Lifecycle
After evaluation, individuals are assigned to species. Each species gets offspring quota proportional to average fitness. Within each species, top performers are selected. Breeding is primarily within species, rarely between. Species without improvement may be eliminated.
Dynamic Threshold
The compatibility threshold adjusts dynamically to maintain the target number of species. Too many species increases threshold (merging similar species), too few decreases it (splitting diverse species).
Summary
Functions
Dynamically adjust compatibility threshold to maintain target species count.
Assign a single individual to a species.
Breed offspring within/between species.
Calculate offspring quota for each species based on relative fitness.
Calculate compatibility distance between two individuals.
Calculate NEAT compatibility distance with config.
Eliminate species that have been stagnant too long.
Get the species ID for an individual.
Get species by ID.
Select survivors within a species.
Assign all individuals in population to species.
Generate a summary of species for visualization/logging.
Update fitness statistics for all species after evaluation.
Types
-type fitness() :: float() | undefined.
-type generation() :: non_neg_integer().
-type genome() :: #genome{connection_genes :: [#connection_gene{innovation :: pos_integer() | undefined, from_id :: term(), to_id :: term(), weight :: float(), enabled :: boolean()}], input_count :: non_neg_integer(), hidden_count :: non_neg_integer(), output_count :: non_neg_integer()}.
-type individual() :: #individual{id :: individual_id(), network :: network(), genome :: genome() | undefined, parent1_id :: individual_id() | undefined, parent2_id :: individual_id() | undefined, fitness :: fitness(), metrics :: metrics(), generation_born :: generation(), birth_evaluation :: non_neg_integer(), max_age :: pos_integer(), is_survivor :: boolean(), is_offspring :: boolean()}.
-type individual_id() :: term().
-type metrics() :: map().
-type network() :: term().
-type speciation_config() :: #speciation_config{enabled :: boolean(), compatibility_threshold :: float(), c1_excess :: float(), c2_disjoint :: float(), c3_weight_diff :: float(), target_species :: pos_integer(), threshold_adjustment_rate :: float(), min_species_size :: pos_integer(), max_stagnation :: non_neg_integer(), species_elitism :: float(), interspecies_mating_rate :: float()}.
-type species() :: #species{id :: species_id(), representative :: individual(), members :: [individual_id()], best_fitness :: fitness(), best_fitness_ever :: fitness(), generation_created :: generation(), age :: non_neg_integer(), stagnant_generations :: non_neg_integer(), offspring_quota :: non_neg_integer()}.
-type species_event() :: #species_event{generation :: generation(), species_id :: species_id(), event_type :: species_created | species_extinct | species_stagnant | champion_emerged, details :: map()}.
-type species_id() :: pos_integer().
Functions
-spec adjust_compatibility_threshold(float(), speciation_config(), non_neg_integer()) -> float().
Dynamically adjust compatibility threshold to maintain target species count.
-spec assign_species(individual(), [species()], speciation_config(), species_id()) -> {[species()], [species_event()], species_id()}.
Assign a single individual to a species.
Compares the individual to each species' representative. If compatible with an existing species, joins it. Otherwise, creates a new species.
-spec breed_species(species(), [species()], speciation_config(), pos_integer()) -> [{individual_id(), individual_id()}].
Breed offspring within/between species.
Primarily breeds within species. Occasionally (based on interspecies_mating_rate) breeds between different species.
-spec calculate_offspring_quotas([species()], pos_integer()) -> [species()].
Calculate offspring quota for each species based on relative fitness.
Species with higher average fitness get more offspring slots. This implements fitness sharing / explicit fitness sharing.
-spec compatibility_distance(individual(), individual()) -> float().
Calculate compatibility distance between two individuals.
Uses NEAT-style distance when genomes are available: delta = (c1 * E / N) + (c2 * D / N) + (c3 * W) Where E=excess genes, D=disjoint genes, N=genome size, W=avg weight diff
Falls back to weight-only distance for fixed-topology (no genome).
-spec compatibility_distance(individual(), individual(), speciation_config()) -> float().
Calculate NEAT compatibility distance with config.
Uses speciation config coefficients for the NEAT formula.
-spec eliminate_stagnant_species([species()], speciation_config(), generation()) -> {[species()], [species_event()]}.
Eliminate species that have been stagnant too long.
Stagnant species (no fitness improvement for many generations) are removed to make room for new exploration.
-spec get_individual_species(individual_id(), [species()]) -> {ok, species_id()} | not_found.
Get the species ID for an individual.
-spec get_species_by_id(species_id(), [species()]) -> {ok, species()} | not_found.
Get species by ID.
-spec select_within_species(species(), speciation_config()) -> {[individual_id()], [individual_id()]}.
Select survivors within a species.
Applies elitism within the species - top performers survive.
-spec speciate([individual()], [species()], speciation_config()) -> {[species()], [species_event()], species_id()}.
Assign all individuals in population to species.
This is the main speciation entry point. Called after evaluation to organize the population into species clusters.
Generate a summary of species for visualization/logging.
-spec update_species_fitness([species()], [individual()]) -> [species()].
Update fitness statistics for all species after evaluation.