pulsatrix
Loading...
Searching...
No Matches
neat_speciation.hpp
Go to the documentation of this file.
1
8#pragma once
9
10#include <algorithm>
11#include <stdexcept>
12#include <vector>
13
15
16namespace pulsatrix {
17
28inline double CompatibilityDistance(const NEATGenome& a, const NEATGenome& b, double c1, double c2, double c3) {
29 const auto& conn_a = a.connections();
30 const auto& conn_b = b.connections();
31 if (conn_a.empty() || conn_b.empty()) {
32 throw std::invalid_argument("CompatibilityDistance: both genomes must have at least one connection gene");
33 }
34
35 int max_innovation_a = 0;
36 for (const auto& c : conn_a) {
37 max_innovation_a = std::max(max_innovation_a, c.innovation);
38 }
39 int max_innovation_b = 0;
40 for (const auto& c : conn_b) {
41 max_innovation_b = std::max(max_innovation_b, c.innovation);
42 }
43 int overlap_bound = std::min(max_innovation_a, max_innovation_b);
44
45 int excess = 0;
46 int disjoint = 0;
47 int matching = 0;
48 double weight_diff_sum = 0.0;
49
50 for (const auto& ca : conn_a) {
51 const auto it = std::find_if(conn_b.begin(), conn_b.end(),
52 [&](const ConnectionGene& cb) { return cb.innovation == ca.innovation; });
53 if (it != conn_b.end()) {
54 ++matching;
55 weight_diff_sum += std::fabs(ca.weight - it->weight);
56 } else if (ca.innovation > overlap_bound) {
57 ++excess;
58 } else {
59 ++disjoint;
60 }
61 }
62 for (const auto& cb : conn_b) {
63 const bool in_a = std::any_of(conn_a.begin(), conn_a.end(),
64 [&](const ConnectionGene& ca) { return ca.innovation == cb.innovation; });
65 if (!in_a) {
66 if (cb.innovation > overlap_bound) {
67 ++excess;
68 } else {
69 ++disjoint;
70 }
71 }
72 }
73
74 size_t size_a = conn_a.size();
75 size_t size_b = conn_b.size();
76 size_t larger = std::max(size_a, size_b);
77 double n = (larger < 20) ? 1.0 : static_cast<double>(larger);
78 double w_bar = (matching > 0) ? weight_diff_sum / static_cast<double>(matching) : 0.0;
79
80 return c1 * static_cast<double>(excess) / n + c2 * static_cast<double>(disjoint) / n + c3 * w_bar;
81}
82
86 std::vector<std::vector<size_t>> species;
87};
88
96inline SpeciesAssignment SpeciatePopulation(const std::vector<NEATGenome>& population,
97 double compatibility_threshold, double c1, double c2, double c3) {
98 if (population.empty()) {
99 throw std::invalid_argument("SpeciatePopulation: population must not be empty");
100 }
101 if (compatibility_threshold <= 0.0) {
102 throw std::invalid_argument("SpeciatePopulation: compatibility_threshold must be positive");
103 }
104
105 std::vector<std::vector<size_t>> species;
106 std::vector<size_t> representatives;
107
108 for (size_t i = 0; i < population.size(); ++i) {
109 bool placed = false;
110 for (size_t s = 0; s < species.size(); ++s) {
111 double distance = CompatibilityDistance(population[i], population[representatives[s]], c1, c2, c3);
112 if (distance < compatibility_threshold) {
113 species[s].push_back(i);
114 placed = true;
115 break;
116 }
117 }
118 if (!placed) {
119 species.push_back({i});
120 representatives.push_back(i);
121 }
122 }
123 return SpeciesAssignment{species};
124}
125
134inline std::vector<double> ComputeAdjustedFitness(const std::vector<double>& raw_fitness,
135 const std::vector<std::vector<size_t>>& species) {
136 size_t total_members = 0;
137 for (const auto& s : species) {
138 total_members += s.size();
139 }
140 if (total_members != raw_fitness.size()) {
141 throw std::invalid_argument(
142 "ComputeAdjustedFitness: species must partition exactly raw_fitness.size() individuals");
143 }
144
145 std::vector<double> adjusted(raw_fitness.size(), 0.0);
146 for (const auto& s : species) {
147 for (size_t index : s) {
148 adjusted[index] = raw_fitness[index] / static_cast<double>(s.size());
149 }
150 }
151 return adjusted;
152}
153
154} // namespace pulsatrix
A NEAT genome: its node and connection genes, growable via structural mutation.
Definition neat_genome.hpp:94
const std::vector< ConnectionGene > & connections() const
Definition neat_genome.hpp:134
Definition acquisition_functions.hpp:16
std::vector< double > ComputeAdjustedFitness(const std::vector< double > &raw_fitness, const std::vector< std::vector< size_t > > &species)
Fitness sharing: each individual's adjusted fitness is its own raw fitness divided by the size of its...
Definition neat_speciation.hpp:134
SpeciesAssignment SpeciatePopulation(const std::vector< NEATGenome > &population, double compatibility_threshold, double c1, double c2, double c3)
Groups population into species: each genome joins the first existing species whose representative (th...
Definition neat_speciation.hpp:96
double CompatibilityDistance(const NEATGenome &a, const NEATGenome &b, double c1, double c2, double c3)
Compatibility distance: delta = c1*E/N + c2*D/N + c3*W_bar, where E is the count of excess genes (inn...
Definition neat_speciation.hpp:28
NEAT genome (Stanley & Miikkulainen, "Evolving Neural Networks through Augmenting Topologies,...
One connection in a NEAT genome: an edge between two node IDs, its weight, whether it is currently ac...
Definition neat_genome.hpp:38
Population grouping into species: each inner vector is a list of indices into the population vector t...
Definition neat_speciation.hpp:85
std::vector< std::vector< size_t > > species
Definition neat_speciation.hpp:86