diff --git a/setup.py b/setup.py index c1882fc..12afc3a 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setup( name='sneat', - version='0.0.6', + version='0.0.7', packages=find_packages(), package_data={'sneat': ['default_config.ini']}, install_requires=[ diff --git a/sneat/default_config.ini b/sneat/default_config.ini index 77091f1..f15319c 100644 --- a/sneat/default_config.ini +++ b/sneat/default_config.ini @@ -23,4 +23,6 @@ remove_node=0.08 [Evolution] max_generations = 100 -max_fitness = 4 \ No newline at end of file +max_fitness = 4 +max_stagnation = 15 +min_species = 3 \ No newline at end of file diff --git a/sneat/evolve.py b/sneat/evolve.py index 9ec0cec..58ff2bf 100644 --- a/sneat/evolve.py +++ b/sneat/evolve.py @@ -38,11 +38,11 @@ def load_checkpoint(): def print_stats(pop): print(f'\n\n[i] Gen. {pop.generation}:') - headers = ['Species', 'Members', 'Best Fitness', 'Average Fitness'] + headers = ['Species', 'Members', 'Best Fitness', 'Average Fitness', 'Stagnation', 'Best Complexity'] for s in pop.species: s.members = sorted(s.members, key=lambda x: x.fitness, reverse=True) species = sorted(pop.species, key=lambda x: x.members[0].fitness, reverse=True) - data = [[s.id, len(s.members), round(max(g.fitness for g in s.members), 2), round(np.mean([g.fitness for g in s.members]), 2)] for s in species] + data = [[s.id, len(s.members), round(max(g.fitness for g in s.members), 2), round(np.mean([g.fitness for g in s.members]), 2), s.stagnation, f'{len(s.members[0].network.nodes)}n + {len(s.members[0].network.connections)}'] for s in species] print(tb(data, headers=headers)) print('-' * 55) diff --git a/sneat/population.py b/sneat/population.py index b88aaaf..71e3cd1 100644 --- a/sneat/population.py +++ b/sneat/population.py @@ -42,13 +42,30 @@ def reproduce(self): for g in self.genomes: g.normalized_fitness = (g.fitness - min_fitness) / (max_fitness - min_fitness) - # assign adjusted fitness scores for s in self.species: - s_size = len(s.members) + # bump stagnation + if s.members[0].fitness > s.best_fitness: + s.best_fitness = s.members[0].fitness + s.stagnation = 0 + else: + s.stagnation += 1 + + # assign adjusted fitness scores + s_size = len(s.members) for g in s.members: g.adjusted_fitness = max(g.normalized_fitness / s_size, 0.0001) # avoid division by zero + # remove stagnant species + while len(self.species) >= self.config.getint('Evolution', 'min_species'): + stagnant_species = [s for s in self.species if s.stagnation >= self.config.getint('Evolution', 'max_stagnation')] + stagnant_species = sorted(stagnant_species, key=lambda x: x.best_fitness, reverse=True) + if not stagnant_species: + break + extinct = stagnant_species.pop() + self.species.remove(extinct) + print(f'[i] Species {extinct.id} went extinct') + # perform reproduction inside of each species offspring = [] for s in self.species: diff --git a/sneat/species.py b/sneat/species.py index f790e5b..d1ba311 100644 --- a/sneat/species.py +++ b/sneat/species.py @@ -4,6 +4,7 @@ def __init__(self, representative, callbacks): self.members = [representative] self.id = callbacks['get_next_species_id']() self.stagnation = 0 + self.best_fitness = float('-inf') def add_member(self, genome): self.members.append(genome) \ No newline at end of file